How to share dependencies in a Modularized Android App - java

I have an Android project which is architectured in a Modularized way. I have modularized the projects by dividing their source code between multiple Gradle modules, following the clean Architecture.
Here is the structure of the App.
The top module in this hierarchy, App is the one that no other module depends upon, is the main module of your application. The lower level modules domain and data do not depend on the App module, where the App module includes the data and domain modules. I have added the below code in the build.gradle of the app module
implementation project(':domain')
api project(':data')
Now, I'm having some issues with maintaining dependencies across each module. Since each of them is an individual android module, each of them having its own build.gradle. The App module can use classes in the data and domain modules. But, I have some general purpose classes, (Such as some annotations, Utilities, Broadcast classes, Dagger scopes etc) which I want to make use in all the modules. But these are the issues I'm facing
Since these classes are contained in the main module app, I cannot
access these in my data and domain, because those modules do not
depend on the higher layer app
Any libraries I'm using in all the layers (eg: RxJava) needs to be
included in the build.gradle of each module
As a solution for this I thought of adding one more android module, say common which will be containing all my general purpose classes as well as the libraries which I use in all the modules.
All my other modules app, domain and data will be having this module as a dependency.
implementation project(':common')
So, any global libraries and classes will be added to this module and each of the individual modules will have only module-specific classes.
Is that a good approach? Or is there any way to solve this issue efficiently?

We recently encountered this problem, as we transitioned to a multi-module project for reuse, build time optimisation (unchanged modules aren't recompiled), etc. Your core goal is to make your app module as small as possible, as it will be recompiled every time.
We used a few general principles, which may help you:
A common base-ui module contains the primary strings.xml, styles.xml etc.
Other front-end modules (profile, dashboard, etc) implement this base-ui module.
Libraries that will be used in all user-facing modules are included in base-ui, as an api instead of implementation.
Libraries that are only used in some modules are added as dependencies only in those modules.
The project makes extensive use of data syncing etc too, so there are also base-data, dashboard-data etc modules, following the same logic.
The dashboard feature module depends on dashboard-data.
The app module depends only on feature modules, dashboard, profile, etc.
I strongly suggest sketching out your module dependency flow beforehand, we ended up with ~15 or so modules, all strictly organised. In your case, you mentioned it's already quite a large app, so I imagine app needs feature modules pulled out of it, as does domain. Remember, small modules = less code to be recompiled!
We encountered some issues with making sure the same version (buildType, flavors) of the app was used across all submodules. Essentially, all submodules have to have the same flavors and buildTypes defined as the app module.
On the other side of the coin, multi module development does really make you think about dependencies, and enforces strict separation between features. You're likely to run into a few unexpected problems that you've never considered before. For example, something as simple as displaying the app's version suddenly complicates (disclaimer: my article).
This article also helped us decide on our approach. The article you linked also seems to be an excellent resource, I wish it had existed when we'd transitioned!
After comment discussion, here's an example diagram (with unfortunate untidiness, but enough to illustrate the concept. Note that distinguishing between api and implementation would be a good next step):

Related

Is it possible to load and unload jdk and custom modules dynamically in Java 9?

I am beginner in JPMS and can't understand its dynamism. For example, in current JVM instance moduleA.jar is running. moduleA requires only java.base module. Now, I want
to load dynamically moduleB.jar that needs java.sql module and moduleC.jar
execute some code from moduleB
unload moduleB, java.sql, moduleC from JVM and release all resources.
Can it be done in Java 9 module system?
This is an advanced topic. At a high-level, the module system is not dynamic in the sense that individual modules can not be unloaded or replaced in a running VM. However, you can use the API, specifically java.lang.module.Configuration and java.lang.ModuleLayer to create dynamic configurations of modules and instantiate them in a running VM as a layer of modules. In your scenario, then you may create a layer of modules with modules B and C. This layer of modules will be GC'ed/unloaded once there are no references to them.
As I said, this is an advanced topic and it's probably better to spend time mastering the basics, including services, before getting into dynamic configurations and module layers.
The other answer is fully correct, but please note that "in the end" these things didn't really change.
Before Java 9 you could use custom class loader instances to achieve something like this. That is for example how application servers such as Tomcat allow you to re-deploy an application - by basically throwing away a whole "context" that was initially "built" using a specific class loader instance.
With Java9, this concept is described using that layers abstraction - but in the end it still means that custom code needs to provide all the implementation of actually creating layers with different class loaders.
And for some further read on layers see this answer that I gave some time back on a similar question (which focused on how to use different versions of the same module within a single application).

Play Framework project composition

I have 2 projects, which are developed using PlayFramework 2.4. Although they are completely separate in concept, they share some common features, like evolution management (Liquibase), CRUD administrative mechanism, notification (email, sms) mechanism, etc. So, it was decided to split every project in 2 modules: common "core" module, which holds all described logic, and "project" module, which hold project-specific services, templates, views.
Recomended approach for achieving this in Play Framework is "subproject" concept. But it's clearly not an option, due to at least two reasons:
Projects are developed by different teams, that's why they they can't be located in one directory structure
These 3 modules ("core" and 2 "project" modules) MUST be versioned in separate VCS repos (Mercurial)
My current solution is to create core module, and provide it as a dependency in "project" Play application. An though this approach partially works, there are major downsides:
If you add routes file in module, they will override project routes file
You cant place views in core module, because due to fig.1 you cant access public assets
Due to downside n.1 and 2, you cant place Controllers in core module, because you cant specify a view to render
static assets (public directory) is not included in module distribution
I'm forced to copy common templates into both projects, I practically can't write common controllers which is annoying SO much
Appreciate any help. Maybe this can be achieved in some sort of highly-custom build and publish process for the core module?
I think you should not add the role core project as a dependency for the other 2 projects.
You could break the core project into core classes and core resources.
The templates and views in play framework are compiled classes, so you can pack then in a jar perfectly. The templates you create would be packaged alongside the core classes (or you can break them too). You can publish this jars into a dependency application like artifactory or nexus and import in the other projects. For the resources, you can package them like webjars do. So you can access them from any other view of your other projects.

Maven modular project share classes across modules

I am working on a Java maven built project, which consists of several modules. I (as many before me) face this issue that I have classes that are used in multiple of modules. I wish to find an elegant solution to the issue of sharing classes across all modules.
I am aware that this is possible to be accomplished by making another module called for example common where I would put all shared classes. After this module can be compiled into separate jar and can be used as dependency in other modules.
However I do not find this solution elegant enough and am looking for a more direct sharing. This essentially means that I would like to have those classes as separate module common, but this module would not be compiled as separate jar, instead those classes would be directly included into compilation/packaging of all depending modules.
Is this possible to achieve using maven?
UPD: To add an example why I do not find mentioned above way as acceptable - when writing code and mid way realize that some changes should be done to the common classes, all IDEs would require after those changes to run install goal on common module in order to have it as compiled jar in classpath (so that those changes will be visible in other modules). This is just one of the examples why I find this way inconvenient and am looking for more elegant solution.

Add extra dependency on war building

I have maven project with next modules: app, app-impl, web. I build web with maven-war-plugin. Now app-impl depends on app. web depends on app-impl.
But web should depends only on app. By some magic maven-war-plugin should include app-impl in war. Could I do it without additional maven module?
I absolutely agree with #Torsten, but maybe there is something valid in your request:
when using IDE to develop the project, you can require it to stop offering your implementation classes in code completion lists.
If this is your reason, just add both dependencies, and for the implementation one, specify <scope>runtime</scope>.
This ensures that:
your app module get to classpath of javac, but app-impl does not
both app and app-impl will be placed under WEB-INF/lib/ in your war
IDE (if properly implemented) will not offer you completions from app-impl
This definitely does not save you keystrokes, but gives you a better pom, carefully modelling the reality.
IMHO having a dependency on app in web does not reflect the reality, as your web-application really depends on having an implementation of the interfaces I assume you have defined in app.
Consider having an alternative implementation in lets say app-impl2. How can maven possibly decide which implementation it should choose?
So having web depend on app-impl is to me the way to go and given the above setup also should work out of the box.

Repository layout for large Maven projects

I have a large application (~50 modules) using a structure similar to the following:
Application
Communication modules
Color communication module
SSN communication module
etc. communication module
Router module
Service modules
Voting service module
Web interface submodule for voting
Vote collector submodule for voting
etc. for voting
Quiz service module
etc. module
I would like to import the application to Maven and Subversion. After some research I found that two practical approaches exists for this.
One is using a tree structure just as the previous one. The drawback of this structure is that you need a ton of tweaking/hacks to get the multi-module reporting work well with Maven. Another downside is that in Subversion the standard trunk/tags/branches approach add even more complexity to the repository.
The other approach uses a flat structure, where there are only one parent project and all the modules, submodules and parts-of-the-submodules are a direct child of the parent project. This approach works well for reporting and is easier in Subversion, however I feel I lose a bit of the structure this way.
Which way would you choose in the long term and why?
We have a largish application (160+ OSGi bundles where each bundle is a Maven module) and the lesson we learned, and continue to learn, is that flat is better. The problem with encoding semantics in your hierarchy is that you lose flexibility. A module that is 100% say "communication" today may be partly "service" tomorrow and then you'll need to be moving things around in your repository and that will break all sorts of scripts, documentation, references, etc.
So I would recommend a flat structure and to encode the semantics in another place (say for example an IDE workspace or documentation).
I've answered a question about version control layout in some detail with examples at another question, it may be relevant to your situation.
I think you're better off flattening your directory structure. Perhaps you want to come up with a naming convention for the directories such that they sort nicely when viewing all of the projects, but ultimately I don't think all of that extra hierarchy is necessary.
Assuming you're using Eclipse as your IDE all of the projects are going to end up in a flat list once you import them anyway so you don't really gain anything from the additional sub directories. That in addition to the fact that the configuration is so much simpler without all the extra hierarchy makes the choice pretty clear in my mind.
You might also want to consider combining some of the modules. I know nothing about your app or domain, but it seems like a lot of those leaf level modules might be better suited as just packages or sets of packages inside another top level module. I'm all for keeping jars cohesive, but it can be taken too far sometimes.

Categories

Resources