I'm reading the source code of Android : Clean Architecture, mostly to learn how to properly organize an application into layers, and for the MVP pattern, and also to match it with what I've been reading on MVP here.
However, as pretty as I find the structure, I don't really understand the benefit of order a single application into multiple sub-projects or modules. Considering they (data, presentation, domain) depend on one another, and eventually will be part of the same executable, it looks more like configuration hell.
dependencies {
...
compile project(':domain')
compile project(':data')
What are the benefits of compartmentalizing an Android application into multiple subprojects (modules), rather than keep them in one project but separate them merely by packages?
Actually this question is not Android-specific but more software architecture related as it applies to almost any software you develop (e.g. why does any app consists of several modules and not all in one package).
Splitting the code into modules would provide you at least the following benefits (these are the first 3 that comes to my mind):
Clear isolation between the modules. The whole goal of the MVP pattern is to make sure your business logic and presentations layer are not tightly coupled together. By defining these in different modules it makes this separation more clear and encourages you to stick to this pattern.
Code reuse - consider the case where you have one application that you'd to sell for several customers, but each of these customers would like to get a different look and feel. If you'll have all of your code in one monolithic project you'll have to either have many forks of your project, one for each customer or otherwise bloat the provided app code with the customization options. Now, if on the other hand you had separated your modules, then you could just prepare a different presentation layer for each customer and bundle it with the other common modules.
Splitting your project into sub-project allows more efficient build and testing as you can rebuild and test only the modules that changed rather than recompiling the whole project whenever there's a change in one of the files.
I hope that this makes sense to you.
The main benefit of using multi-module projects, instead of just packages, is that the code usage between modules goes in one direction only.
Any code inside the module can have a back-and-forth relationship:
A -> uses classes from -> B
and
B -> uses classes from -> A
If A and B are in separate modules, this only goes one-way:
A -> (uses classes from) -> B...
But B can't see anything in A.
This splits up your thinking into smaller, bite-sized chunks. If you need to understand how a class in module B works, you only need to look at the other classes in B.
But in the first scenario, you have to look at the all the classes in both A and B.
As an added bonus, if you do want to reuse any code, you can copy that module right over to the next project.
I usually put the bulk of my code in modules, with a thin app layer connecting them all together. I recommend using dependency inversion to make any connections between modules.
Another Android specific advantage of splitting into modules apart from faster build time is that the update size of your app will be less.
Related
I'm almost finishing migrating my app to MVVM with databinding and livedata (still java though) and now I have much more than a decent architectured Android app (which I'm showing below). I'm happy with that, but would like to go one step further.
Talking about clean architecture, I'm trying to figure out how to do a proper separation of concerns in Android (database, business, services, etc).
I work in .net and in that platform, what you do to separate layers is to create a different proyect for each layer (database, bussiness, presentation) and then you reference them in the correct order, but projects are mostly independent one of the others.
In Android, and as far as I know, you have an app module and even though I have a nice package agrupation, all is "together" into the same project.
I'm not sure if this is the best approach to really follow clean architecture principles. I've heard about Dagger, heard you can create modules with it, but not sure if it is intended for what I'm trying to do.
Any help/hints about a good way to implement separation of concerns in Android?
My current app structure:
com
xxx
xxx
dto
class_1_dto.java
...
class_N_dto.java
helpers
helper_http.java
helper_json.java
helper_utils.java
helper_enum.java
helper_file.java
helper_smtp.java
helper_date.java
...
model
model_class_1
model_class_2
...
all_model_classes_linked_to_AWS_database
poco
some_poco_classes
repository
aws
IAWSDAO
AWS_Repository
...
all_stuff_related_to_AWS_database_query
local
model_class_1_repo
model_class_2_repo
...
all_stuff_related_to_SQLite_database_query
services
model_class_1_serv
model_class_2_serv
...
all_stuff_related_to_local_repos_query
ui
activities
activity_1
activity_1_viewmodel
activity_2
activity_2_viewmodel
...
activity_N
activity_N_viewmodel
component
custom_view_1
custom_view_2
...
helpers
view_helper_1
...
view_helper_N
assets
res
...
You can segregate your concerns like (app, core, network, service, repository) by making multiple modules. Just like 'app' is a module, you can create an independent module for each concern and you can use Koin for dependency injection between the modules.
For reference here is an example github repo:
https://github.com/Fahad-github/Bykea-CaseStudy-MusicApp
Square Inc. has presented it's internal modular architecture at Droidcon SF'19:
https://www.droidcon.com/media-detail?video=380843878
However, I'm a bit confused with some bullets. Could you please help me?
Why do they actually need :wiring modules? I find it adding complexity:
you get extra gradle module for each new feature
you have to make a sort of global injection into your Fragments somewhere in :app, because Fragments defined in :impl modules cannot access it's DaggerComponent, which is defined in :impl-wiring modules. :impl doesn't depend on :impl-wiring, because the dependency is reversed.
you cannot have an Android Dynamic Feature modules, because they should know about it's DaggerComponent in order to inject it's Fragment. But there is no way to do such injection from :app module, which is base-module for Dynamic Features.
so why :wiring modules at all?
One can merge :impl and :impl-wiring, or :fake and :fake-wiring together to eliminate all the issues mentioned above. And also, in :demo-apps one could just have a dependency on either :impl or :fake``, and not on :impl-wiring(or:fake-wiring```).
The creation of this type of modules is to separate even more. With this you generate an abstraction of the type of component you use (koin, dagger) and how. If the project is large, it makes sense to do it.
Currently I generate the following flow of dependencies between modules:
WARNING: Check the directionalities well.
:feature-A:open <- :feature-A:impl -> :feature-A:impl-wiring
:feature-A:impl-wiring -> :feature-A:impl, :feature-A:open
:app -> :feature-A:open, :feature-A:impl-wiring
I'm still not sure if app should depend on open and impl-wiring, or which app should only depend on open and open from impl-wiring.
Eventually, I came up with the following solution:
each feature consists of the following gradle-modules:
api
impl and fake
data:api
data:impl1 ... data:implN and data:fake
data:wiring
ui
demo
So, here api, impl and fake as usual, but I've my data layers separated. I bought myself that I need multiple different implementation of data layers sometimes, for example - if I develop Stock-Charts App, I could rely on Finnhub Open API or MBOUM API or provide fake implementation.
Thus I have data:api, data:implX. Indeed, data:api defines FeatureRepository interface (one or many) and data:implX provides actual implementation for them. In order to bind interface and implementation, I use data:wiring, which defines Dagger modules and component(s). In addition, I keep the same package names within each data:implX module in order to "write-once" the data:wiring module. And to replace one implementation with another, I just change a single line in data:wiring/build.gradle which states a sort of:
implementation project(":data:implA")
to
implementation project(":data:implB")
Also, to break the confusion mentioned in my original question, I introduce ui module, which contains some Views of a particular feature. Fragments go in demo (a standalone app to test feature) or in ui, they refer to viewModel which have some bindings ctor-injected from Dagger component of a feature. But the UI and library are separated here. Fragment instantiates a dedicated Dagger component that uses component dependencies to refer to feature's library bindings, such as interactor or repository etc.
So, to wrap up - separation between UI and business logic implementation (a "library") for each feature makes it possible to solve the issue. Feature's api declares an entry point to it's functionality as a library, and it's global access via Dagger multibindings from :app. So it can be used further in any :demo, :ui and :dynamic-feature.
I'm creating a sample app which I'm following the clean architecture, my app structure now is like this :
:app
:firebase
:library_base (which contains all the baseActivity, baseFragment, etc.. I know I could create a base_ui, base_data, module, but let's first solve this question that I'm having right now)
:networking (which contains retrofit stuff)
feature1
feature2
....
So now, my question is, now I do not need a core module, but in case one of my featureX, needs a dependency from featureY, what should I do in this case? I'm used to have a core on my app that contains stuff like LoginSettings which contains data from the user logged and things like that, and now if I'd have to do this I could not because featureX can not depend on app, so that's why I'm thinking about adding a core module and insert all of the needs from featureX there so they can use it. (Yes, I said "all of the needs", I did not mean to create a god module, but just to start the app).
Is it necessary to create a core module? I'm not using dynamic feature tho, and also I'm seeing that on each build.gradle files I'm duplicating a lot of dependencies...
From now in the app I have everything with api should I put that in the core?
One approach would be create modules "with UI" (call them features) and "without UI" (call them libraries).
Rule is "features" can not depend on each other directly, they can only depend on "libraries". While "libraries" can depend on each other.
Now LoginSettings, SessionConfigs kind of things can be a "library" where multiple "features" (UI) can depend on. Not all features has to depend on that "library".
So I have an interesting question more or less to the high level design of using Android Libraries vs product flavors vs product variants etc.
Right now my problem is that I am trying to build a second Android application (after building my first) one where in many cases I can reuse a majority of my non UI code (IE activities, styles etc.) and reuse a small portion of the UI code.
Right now I see two different strategies, one being make a new application + Android library for the shared code (resulting in basically my POV two new GIT repos along with two corresponding projects) or take my original application and write a few productFlavor / productVariants and sort of tweak things as needed (this would allow me not to have to make two new GIT repos + project setups).
What do folks usually do in this situation?
Update:
Maybe a project with each submodule would work best as separate apps while moving the central library (shared code) to a library module.
Im going to develop a very large scale android project, which has thousands of classes and resources. Im planning to separate application in to modules and develop them separately as library projects. Later combine them together. (Application may contain 5 - 6 modules, so planning to create 5 - 6 library projects and combine them)
Is this approach ok? or android experts, please suggest a way to maintain and develop such a big project?
Edit:
Libraries hold shared code for multiple applications -> Yes agreed 100% true
But this project is like combination of several projects.
Its like this:
Home Screen Dashboard has 8 buttons which represents 8 modules
you click on one button - > it opens up an activity and it has its own thousands of fragments, layouts, drawables etc, which is independent from other modules
so likewise i have non interdependent use cases which can be separated easily, and 4 - 5 developers are going to be involved this project, so if I can separate in to several library projects, i can simply allocate developers easily based on modules(library projects)
So one approach is to create one project and create package structure by modules
com.name.something.Module1
under this package i have
com.name.something.Module1.activity
com.name.something.Module1.util
com.name.something.Module1.widget
com.name.something.Module1.data
com.name.something.Module1.dao
and module 2
com.name.something.Module2
com.name.something.Module2.activity
com.name.something.Module2.util
com.name.something.Module2.widget
etc.
so this is first approach but each module has thousands of classes and resources, layout xml files etc.
The other approach is to separate modules as library projects.
I dont know how large scale projects maintain their codebase, like facebook, twitter etc.
Please advise.
Libraries hold shared code for multiple applications... if you are entirely focused on a single application then there's no point in separating your code into 5-6 library projects.
One common way Android developers separate their project is by making sub-packages for different components. For example, custom Views and adapter's go in com.package.name.ui, utility packages go in com.package.name.util, etc. Other than that, you just have to be smart... starting an app from scratch that will have "thousands of classes" sounds pretty ambitious and there is not really any single piece of advice that will make your life easy.
Is each module separated from each other or do they share data (e.g. the same database)? If they are separated I would suggest to create 8 separate apps, which would reduce the memory footprint of your app and would improve launch time.
If some, or all are using the same database, you might be able to create a database on the SD-card and use it from each separate app.