I understand that Philipp has its own opinions on this subject, but calling these approaches "fatal" is wrong. They are not even mistakes. Whether you choose data modules, common modules, feature modules or hybrid depends on the project, period! e.g MVP, AB-Testing, hardening, libs, integrations, sdks may have different requirements and modularization patterns are changing along with development. Generally, you should to aim for low coupling and high cohesion, with some trade-offs. Anyone working on modularization of a large App module architecture with Dagger2 or Hilt knows how hard it can be. Cyclic dependency errors, missing bindings, moving assets, dependencies, sub-module to App communication problems are common. It is true, that modularization from day one takes more time, but this is nothing comparing to modularization of a monolithic application. Deciding when is the best time to start modularizing and explaining why you need to add this refactors to sprint planning are not easy as well. If you use mediator modules, common modules and feature modules wisely, right from the beginning, it will pay off.
He's an youtuber, he has to bring viewers, can't you make the difference? he knows this already, but he is also trying to create content and attract people so yeah, redundant comment imo
Application life cycle needs to be taken into account. Monolithic application will take less time to finish and that might be what bussines side is expecting. Proof of concept before company allocates more money into specific application idea. Instead of allocating a lot of resources into something that they are not sure about.
Surely modularisation requires some work and may not always be worth the hassle. However, extracting a platform-independent business logic SDK into a separate module so that it is isolated from the application's platform-specific code somehow still sounds like a good idea to me for very many projects.
I wouldn't say that dividing application into data, domain, presentation etc. is wrong. For example one person can work on data, domain and another on presentation module. The only place where the modules meet is interface. Modules are good for dividing the work in team and avoiding merge conflicts. How often did you reused module any way ?
Thanks for such a comprehensive answer to multi-module approach, cause in most of articles I've faced, modularization by layers everywhere is positioned as a just alternative to modularization by features, and I have the same concerns about modularization by layers, that you just described in video. Very cool!
@@imashnake_7151 there are people who prefer videos in learning some stuffs, so for your perspective it's not necessary but for others who have a different learning curve, it's better
@@imashnake_7151 Everything feels unnecessary as Android documents. Their github repository and codelabs are already enough. So why are there so many Android tutorials???
Thanks for the great content! I didn't use modularization yet, but i will definitively be looking for or even creating CLI tools to help me build the modules if it takes that much time. I understand that early modularization can be bad, but "costing a lot of time" to setup the modules shouldn't be the reason, and if it is then we need a tool to fix this situation, the best way to handle wasted time to do "setup stuff" is investing even MORE time to build tools to automate the boring parts, because you only write it once and it helps in every other project. In the end it is just boilerplate code for Gradle to be happy and the modules work properly. Other technology stacks already have the tools necessary so that the developers don't need to manipulate code to create/attach a module as a dependency. But as I said, i didn't work with modularization yet, maybe such tools are not trivial to create in the Android&Gradle ecosystem (for such a mature tool like Gradle it shouldn't be a challenge though).
TBH I am not a big fan of having the core modules, because, in the end, this will become a kinda god module. All the features modules will be dependent on this module. And change any change in the core module would frequently happen because everyone will put all commons methods utility functions will lead to recompilation of this core module and because of the dependency graph of modules with core can't achieve the parallelism until this god module gets compiled.
I would instead recommend having a top level "domain" container module that contains various submodules parts of your domain("domain:a", "domain:b"), and a top level "data" container module that contains various types of data modules associated with your domain ("data:a1", "data:a2", "data:b1", etc.). And finally a third presentation or feature module container that contains your various features modules. The feature based structure here assumes that your domain is tied to a specific feature, which is not always correct. For example, think about a "user" domain (and associated object / use cases) containing information about your "user" account. You would want to reuse that "user" domain object (and underlying data layer) across multiple features, perhaps in the user account page (where you display the full user details and where they can edit them) or in some home page where you welcome the "user" by their first / last name and wish them a happy birthday once a year (using their DoB). In all likelihood this would be coming from a single user domain object (cached). Your domain and the associated use cases is potentially going to be reused across multiple features / screens (as it should be), and so shouldn't be tied to a specific screen or feature. The package structure presented here (although it works) would suggest to other developers working on the project that presentation / domain / data are one and the same. If you reverse that package structure (domain at the top) then you will start seeing your screens / features as just ways to display and assemble multiple domain objects together via a view model or similar (almost like architectural composition).
I wonder how teams work in most cases. Do you have a team (or developer) per screen? Or do you have a data specialist and a ui specialist. I would think that's an important consideration in setting up modules. Another question is: what is likely to be stand-alone. Obviously everything in an app is linked to each other in some way. But I would think that a screen, from ui to (local!) data, may be relatively stand-alone. On the other hand, if the data is a large enterprise db, then the DB isn't even owned by the android developers - they may have to request db changes to the db-administrator. Even the public interface to the db (a REST server, cloud service,...) may be a team that 'serves' not only android but other 'clients' as well. As for compile time in a one-dev project, I would think that a screen (feature) is more stand-alone than a layer, and I'd be inclined to make modules vertical (module per feature) rather than horizontal (module per layer). Having a full grid-split (vertical as well as horizontal) seems overkill for a one-man project. Anyway, it's a classification problem (Lego blocks per size or per color?) and therefor no solution is perfect. For a specific (beginner) project, I've decided to keep the data in one module, as the DB structure is pretty much fixed and won't change often. I may split the screens in separate 'features' modules, as I won't be tinkering in one screen while working in another one. But maybe that creates too much gradle overhead.
Hi @Phillip, one Question... Looks like "Onboarding" module was created as "New Module" (Phone & Tablet), but the inner "modules" were created as "Android Libaries" , is that correct ?, if it so, why "core" is created as library ? Thanks!
one must have to deal with the layer-based modularization when it comes to Kotlin multiplatform. otherwise yes i totally agree with you on the drawbacks of such strategy :) good video
thanks for sharing great knowledge sir. sir i created an app for multiple people from the same source with different name, themes, server and google's .json configuration file. the problem is when i update one i need to update others too. is there a why to create multiple apps from the same source with different name, themes, server and google's .json configuration file from one project with different source sets. thanks.
Hello Philipp, interesting video ! I have been using for years a layer-base multi-module architecture and I'm happy with it. This feature-base management sounds interesting. I will give it a try. One question though. As said in your video, you structure each module by layer. Does it mean you create a dedicated retrofit instance for each data layer ? What about Room instance and di directory ? Thanks in advance;
If a Retrofit instance is only needed for one feature it goes in that feature module, if not you can use a core module for all shared code :) Same for DI
@@PhilippLackner But if you create a Retrofit instance in core module, then it will be accessible from everywhere because core module is a dependency in all other modules (e.g. :app module, :tracker:tracker_presentation, etc). This will brake the separation of concerns.
I did not understand the statement regarding layer-based packaging like "if something is changed in data layer - the whole module is rebuilt and that's not what we want". Really? Is there the other way around it? If we follow the Clean Architecture and dependency rule, presentation will depend on data and data will depend on domain (presentation -> data -> domain) so if some module uses only data layer of the other module, that module will not be rebuilt if presentation module is changed. That's the benefit. Example: 1) moduleA depends on moduleB:data. 2) moduleB:presentation is changed 3) when building moduleA moduleB:data won't be rebuilt; (please correct me if I am wrong and the building process works differently)
WRONG! We always start all our projects with multi-module and we benefit from it everyday going forward. YES ... by feature is correct but have never seen anyone do it any other way.
Yeah, modulatization helps to build projects faster. But if your project has 300+ modules, it won't work. Because the configuration will take more time. The Gradle team seems aware of this issue, but I don't know if they are working on it
Hi Bro, hope you are well.Maybe i won't get any answer from you, even asking. I want to make a multi module app, please suggest me any of your videos, because i have not connected with android since last 5 months. i want to start android again. By the way, i am from India . Please reply bro, it's really necessary.
Hi Philipp, nice course btw. In your course only the tracker module uses room and I'm curious to know how you handle singletons like Room database and retrofit which are expensive to create with this approach
Hey Philip, how would you do top level navigation then with this feature based modularization? Eg moving from onboarding to home screen. Keep in mind the onboarding screen shouldn't know what activity it opens after it's done.
You should have a separate module, call it as "navigation module" this should be implemented in the Master App (the main app that handles all the other features or modules). Just keep informing the implementation in the master app about activation by different modules (or moving to and from another module).
Great video, and very informative as always! You explain very well so I have a video request/ a topic that does not have a lot of information available. Would you consider doing a video about firebase authentication and firebase registration of users using jetpack compose ? Would be awesome if you could! Anyway, keep those videos coming :D
Phillip, do we need a domain layer .. if I have sdk style library modules. I went to droidcon .. 2018 .. when they said it's useless to create Domain abstraction in a multi modular architecture..
Hi Philipp, thanks for the video. In my case I have in my personal application also modularized the project, but a friend when trying to clone the project one of the modules fails to compile, the module remains with the blue square icon, instead of the 3 colored bars, when trying to run the project returns this error: Could not resolve project :common. Required by: project :app project :app > project :football project :app > project :adobeclippers > No matching configuration of project :common was found. The consumer was configured to find an API of a component, preferably optimized for Android, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug', attribute 'com.android.build.api.attributes.ProductFlavor:environment' with value 'footballApp', attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.4.0', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm' but: - None of the consumable configurations have attributes. If you could give me a hand in case something like this has ever happened to you? In the settings.gradle all the modules appear correctly. include ':app' include ':common' include ':styles' include ':repositories' include ':player' include ':football' include ':analytics' include ':adobeclippers'
I agree that these are good generic advices but I don’t think it’s the best for all cases. At work we knowingly (as in, we knew the its drawbacks) chose a “pres domain data like” (not exactly) module structure because it achieved what we wanted without complicating the project with one module per feature which in our case had no benefits. So in the end, nothing is wrong.. in a given context, something can be correct which would be wrong in other contexts. Just wanted to add my thoughts 😁 But it’s a good video as always 👌
And for sure, you'll find a use case for everything that's justified, but there's still a more or less likely factor to it. In the end everything I share on my channel is a result of my experience and opinion 😄
@@PhilippLackner multiple reasons: enforcing separation of concerns between app layers, being able to move to a very possible KMM future easily, having different members of the team work in a lower level layer(one we have specific to our case - yes sometimes splitting work by layer is the preferred way). Having a multi module app allowed us this and the chosen layered module structure avoided the complexity of managing 50 modules each with inner modules that for us wouldn’t achieve anything of interest.
Would be interesting to know how this contrasts with Google-recommended approach to modularize by both, layer and feature.. (ruclips.net/video/16SwTvzDO0A/видео.html)
Not quite true. The main difference is that Kotlin doesn't have a "package private" visibility modifier like Java does, so the closest thing is "internal" which only works for encapsulation inside a module. I wish there were more granular modifiers than this so that a package level would suffice and still be able to be well encapsulated, but alas, this is not the case.
I'm sorry I don't have time to write a better comment but honestly very mediocre video. Allot of the information in the video is lacking and some things is just not fully correct.
Oh dear. This is garbage. 1) His advice is wrong. period. 2) He doesn't understand the need for separation of concerns. To quote one of the founders of UML , "It is to localize change" - think about it Philipp.
I understand that Philipp has its own opinions on this subject, but calling these approaches "fatal" is wrong. They are not even mistakes. Whether you choose data modules, common modules, feature modules or hybrid depends on the project, period! e.g MVP, AB-Testing, hardening, libs, integrations, sdks may have different requirements and modularization patterns are changing along with development. Generally, you should to aim for low coupling and high cohesion, with some trade-offs.
Anyone working on modularization of a large App module architecture with Dagger2 or Hilt knows how hard it can be. Cyclic dependency errors, missing bindings, moving assets, dependencies, sub-module to App communication problems are common. It is true, that modularization from day one takes more time, but this is nothing comparing to modularization of a monolithic application. Deciding when is the best time to start modularizing and explaining why you need to add this refactors to sprint planning are not easy as well. If you use mediator modules, common modules and feature modules wisely, right from the beginning, it will pay off.
He's an youtuber, he has to bring viewers, can't you make the difference? he knows this already, but he is also trying to create content and attract people so yeah, redundant comment imo
Application life cycle needs to be taken into account. Monolithic application will take less time to finish and that might be what bussines side is expecting. Proof of concept before company allocates more money into specific application idea. Instead of allocating a lot of resources into something that they are not sure about.
Yes. Fuc*** click bite title
Surely modularisation requires some work and may not always be worth the hassle. However, extracting a platform-independent business logic SDK into a separate module so that it is isolated from the application's platform-specific code somehow still sounds like a good idea to me for very many projects.
I wouldn't say that dividing application into data, domain, presentation etc. is wrong. For example one person can work on data, domain and another on presentation module. The only place where the modules meet is interface. Modules are good for dividing the work in team and avoiding merge conflicts. How often did you reused module any way ?
Thanks for such a comprehensive answer to multi-module approach, cause in most of articles I've faced, modularization by layers everywhere is positioned as a just alternative to modularization by features, and I have the same concerns about modularization by layers, that you just described in video. Very cool!
Only question I have is that in which case we would require to have layer based modularization?
once I modularize the app, how can i have a team work on one module while not exposing the rest of the app using a VCS (ie:git)?
I think you have missed a thing, if we don't create modules based on layers, how can we expose only domain layer?
it would be great if you could make a tutorial to use kotlin DLS, set up ... kts like in the video above.
Gradle has great documentation that essentially walks you through migrating stuff, a video seems unnecessary.
@@imashnake_7151 there are people who prefer videos in learning some stuffs, so for your perspective it's not necessary but for others who have a different learning curve, it's better
@@imashnake_7151
Everything feels unnecessary as Android documents. Their github repository and codelabs are already enough. So why are there so many Android tutorials???
Thanks for the great content!
I didn't use modularization yet, but i will definitively be looking for or even creating CLI tools to help me build the modules if it takes that much time. I understand that early modularization can be bad, but "costing a lot of time" to setup the modules shouldn't be the reason, and if it is then we need a tool to fix this situation, the best way to handle wasted time to do "setup stuff" is investing even MORE time to build tools to automate the boring parts, because you only write it once and it helps in every other project. In the end it is just boilerplate code for Gradle to be happy and the modules work properly. Other technology stacks already have the tools necessary so that the developers don't need to manipulate code to create/attach a module as a dependency. But as I said, i didn't work with modularization yet, maybe such tools are not trivial to create in the Android&Gradle ecosystem (for such a mature tool like Gradle it shouldn't be a challenge though).
TBH I am not a big fan of having the core modules, because, in the end, this will become a kinda god module. All the features modules will be dependent on this module. And change any change in the core module would frequently happen because everyone will put all commons methods utility functions will lead to recompilation of this core module and because of the dependency graph of modules with core can't achieve the parallelism until this god module gets compiled.
I would instead recommend having a top level "domain" container module that contains various submodules parts of your domain("domain:a", "domain:b"), and a top level "data" container module that contains various types of data modules associated with your domain ("data:a1", "data:a2", "data:b1", etc.). And finally a third presentation or feature module container that contains your various features modules.
The feature based structure here assumes that your domain is tied to a specific feature, which is not always correct. For example, think about a "user" domain (and associated object / use cases) containing information about your "user" account. You would want to reuse that "user" domain object (and underlying data layer) across multiple features, perhaps in the user account page (where you display the full user details and where they can edit them) or in some home page where you welcome the "user" by their first / last name and wish them a happy birthday once a year (using their DoB). In all likelihood this would be coming from a single user domain object (cached).
Your domain and the associated use cases is potentially going to be reused across multiple features / screens (as it should be), and so shouldn't be tied to a specific screen or feature. The package structure presented here (although it works) would suggest to other developers working on the project that presentation / domain / data are one and the same.
If you reverse that package structure (domain at the top) then you will start seeing your screens / features as just ways to display and assemble multiple domain objects together via a view model or similar (almost like architectural composition).
Sure, some dependencies are shared and put in a core module (in my sample here)
How about sharing some useCases or dataSources between features , should we duplicate them?? 🧐
You can make multiple features to depend on one domain or data module
I wonder how teams work in most cases. Do you have a team (or developer) per screen? Or do you have a data specialist and a ui specialist. I would think that's an important consideration in setting up modules.
Another question is: what is likely to be stand-alone. Obviously everything in an app is linked to each other in some way. But I would think that a screen, from ui to (local!) data, may be relatively stand-alone. On the other hand, if the data is a large enterprise db, then the DB isn't even owned by the android developers - they may have to request db changes to the db-administrator. Even the public interface to the db (a REST server, cloud service,...) may be a team that 'serves' not only android but other 'clients' as well.
As for compile time in a one-dev project, I would think that a screen (feature) is more stand-alone than a layer, and I'd be inclined to make modules vertical (module per feature) rather than horizontal (module per layer). Having a full grid-split (vertical as well as horizontal) seems overkill for a one-man project.
Anyway, it's a classification problem (Lego blocks per size or per color?) and therefor no solution is perfect.
For a specific (beginner) project, I've decided to keep the data in one module, as the DB structure is pretty much fixed and won't change often. I may split the screens in separate 'features' modules, as I won't be tinkering in one screen while working in another one. But maybe that creates too much gradle overhead.
When you have many feature based modules, to setup a CI\CD process for this would be a hell I guess ?
Hi @Phillip, one Question...
Looks like "Onboarding" module was created as "New Module" (Phone & Tablet), but the inner "modules" were created as "Android Libaries" , is that correct ?, if it so, why "core" is created as library ? Thanks!
one must have to deal with the layer-based modularization when it comes to Kotlin multiplatform. otherwise yes i totally agree with you on the drawbacks of such strategy :) good video
Great video Philipp. Thank u. But what about the common use cases or data classes?
Please make a video on how to implement stripe payment in the app.
Slow builds alert: move all kapt to separate leaf-module. It will make your builds much faster as they won't compile if you do not change them
thanks for sharing great knowledge sir.
sir i created an app for multiple people from the same source with different name, themes, server and google's .json configuration file. the problem is when i update one i need to update others too.
is there a why to create multiple apps from the same source with different name, themes, server and google's .json configuration file from one project with different source sets.
thanks.
Hello Philipp, interesting video ! I have been using for years a layer-base multi-module architecture and I'm happy with it. This feature-base management sounds interesting. I will give it a try. One question though. As said in your video, you structure each module by layer. Does it mean you create a dedicated retrofit instance for each data layer ? What about Room instance and di directory ? Thanks in advance;
If a Retrofit instance is only needed for one feature it goes in that feature module, if not you can use a core module for all shared code :) Same for DI
@@PhilippLackner But if you create a Retrofit instance in core module, then it will be accessible from everywhere because core module is a dependency in all other modules (e.g. :app module, :tracker:tracker_presentation, etc). This will brake the separation of concerns.
@@da_to why will that break separation of concerns? That just means that the layers are divided. At some point you'll have to accept trade offs
I did not understand the statement regarding layer-based packaging like "if something is changed in data layer - the whole module is rebuilt and that's not what we want". Really? Is there the other way around it? If we follow the Clean Architecture and dependency rule, presentation will depend on data and data will depend on domain (presentation -> data -> domain) so if some module uses only data layer of the other module, that module will not be rebuilt if presentation module is changed. That's the benefit.
Example: 1) moduleA depends on moduleB:data. 2) moduleB:presentation is changed 3) when building moduleA moduleB:data won't be rebuilt;
(please correct me if I am wrong and the building process works differently)
@@dmytromarchuk3023 think it is presentation -> bussines logic -> data
Hey, Philipp, thank you for the video.
It’s always fun and useful to look at some ideas from your perspective. Keep it up.
WRONG! We always start all our projects with multi-module and we benefit from it everyday going forward. YES ... by feature is correct but have never seen anyone do it any other way.
great video brother. please make a video on a simple API call with multi-module. please, please...
Thank you for such a useful video bro! You made my day as usual✊🏻😊😉
Yeah, modulatization helps to build projects faster. But if your project has 300+ modules, it won't work. Because the configuration will take more time. The Gradle team seems aware of this issue, but I don't know if they are working on it
if the project has 300+ modules, then it's modularized in a wrong way
@@kurczaken tell this to the lyft 🙂
Hi Bro, hope you are well.Maybe i won't get any answer from you, even asking. I want to make a multi module app, please suggest me any of your videos, because i have not connected with android since last 5 months. i want to start android again. By the way, i am from India . Please reply bro, it's really necessary.
@@anirbanroy6337 this course is all you need:
pl-coding.com/android-essentials-bundle
Hi Philipp, nice course btw. In your course only the tracker module uses room and I'm curious to know how you handle singletons like Room database and retrofit which are expensive to create with this approach
Your approche makes sens to me a-lot .
Hey Philip, how would you do top level navigation then with this feature based modularization?
Eg moving from onboarding to home screen.
Keep in mind the onboarding screen shouldn't know what activity it opens after it's done.
You should have a separate module, call it as "navigation module" this should be implemented in the Master App (the main app that handles all the other features or modules).
Just keep informing the implementation in the master app about activation by different modules (or moving to and from another module).
Great video, and very informative as always! You explain very well so I have a video request/ a topic that does not have a lot of information available. Would you consider doing a video about firebase authentication and firebase registration of users using jetpack compose ? Would be awesome if you could! Anyway, keep those videos coming :D
Phillip, do we need a domain layer .. if I have sdk style library modules.
I went to droidcon .. 2018 .. when they said it's useless to create Domain abstraction in a multi modular architecture..
It's an optional layer and there's no general rule how you have to structure your apps
Hi Philipp, thanks for the video. In my case I have in my personal application also modularized the project, but a friend when trying to clone the project one of the modules fails to compile, the module remains with the blue square icon, instead of the 3 colored bars, when trying to run the project returns this error:
Could not resolve project :common.
Required by:
project :app
project :app > project :football
project :app > project :adobeclippers
> No matching configuration of project :common was found. The consumer was configured to find an API of a component, preferably optimized for Android, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug', attribute 'com.android.build.api.attributes.ProductFlavor:environment' with value 'footballApp', attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.4.0', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm' but:
- None of the consumable configurations have attributes.
If you could give me a hand in case something like this has ever happened to you? In the settings.gradle all the modules appear correctly.
include ':app'
include ':common'
include ':styles'
include ':repositories'
include ':player'
include ':football'
include ':analytics'
include ':adobeclippers'
mmm
seems the premium course is no longer avaible
A new one is out which is more up-to-date, covers the same and more concepts: pl-coding.com/android-essentials-bundle
@@PhilippLackner Ohhh the tracker one, thanks !! c:
I agree that these are good generic advices but I don’t think it’s the best for all cases. At work we knowingly (as in, we knew the its drawbacks) chose a “pres domain data like” (not exactly) module structure because it achieved what we wanted without complicating the project with one module per feature which in our case had no benefits.
So in the end, nothing is wrong.. in a given context, something can be correct which would be wrong in other contexts.
Just wanted to add my thoughts 😁
But it’s a good video as always 👌
And for what did you need a multi module architecture in that case? 😄
And for sure, you'll find a use case for everything that's justified, but there's still a more or less likely factor to it. In the end everything I share on my channel is a result of my experience and opinion 😄
@@PhilippLackner multiple reasons: enforcing separation of concerns between app layers, being able to move to a very possible KMM future easily, having different members of the team work in a lower level layer(one we have specific to our case - yes sometimes splitting work by layer is the preferred way).
Having a multi module app allowed us this and the chosen layered module structure avoided the complexity of managing 50 modules each with inner modules that for us wouldn’t achieve anything of interest.
Hello
very cool video
Would be interesting to know how this contrasts with Google-recommended approach to modularize by both, layer and feature.. (ruclips.net/video/16SwTvzDO0A/видео.html)
multimodule is overrated. if you can replace module with kotlin package - you don't need module.
Not quite true. The main difference is that Kotlin doesn't have a "package private" visibility modifier like Java does, so the closest thing is "internal" which only works for encapsulation inside a module.
I wish there were more granular modifiers than this so that a package level would suffice and still be able to be well encapsulated, but alas, this is not the case.
I'm sorry I don't have time to write a better comment but honestly very mediocre video. Allot of the information in the video is lacking and some things is just not fully correct.
Oh dear. This is garbage. 1) His advice is wrong. period. 2) He doesn't understand the need for separation of concerns. To quote one of the founders of UML , "It is to localize change" - think about it Philipp.
Ich dachte build Tools mit generate *.iml würde meine build Zeit beschleunigen 😜