If you have been an android developer for around a year or two, you would keep listening to modularization. Here I will discuss the need for modularization, how to approach modularization for your app and benefits of it.
First thing what is module ?
A module is a container that includes source code, resource files, manifest and gradle files. That allows us to break the project into smaller discrete units to manage complexity. Your project can have one or many modules and one module may use another module as a dependency. Each module can be independently built, tested, and debugged.
We all have started once started with writing everything inside the app module. Maybe we never thought about it or neglected it. Having a monolithic module is not a good idea. Monolithic module works but once your application codebase is big, and your team scales well. It becomes harder to add or update new features, build time increases exponentially, refactoring takes a lot of time. Experimenting with a set of the library becomes difficult.
How to approach Modularization architecture
We would start with a simple app structure. Splitting it into app, feature and core modules.
- app-module: This module would be knowing everything about the app as all the feature module would be included here. We are going to use dagger so this module’s responsibility is to initialize the dagger app component. This idea that app-module knows everything we would also as a communication medium between independent feature modules.
- feature-module: This module would be a container for specific features in the app. Ideally, it should be small, independent and effective.
- core-module: This would provide reusable classes for various modules that need to be shared. This can also contain some base classes like BaseActivity or BaseFragment.
Since we see there are not explicit dependencies between each feature module, these modules are agnostic of each other. Handling navigation between them is a challenge.
To get started with navigation and sharing data between independent modules let’s discuss abstraction.
Abstraction is something which keeps complexity hidden from the implementation. This abstraction is the interface to the rest of the world.
Interface allows us to expose necessary functionality that’s ready for consumption for other modules while hiding everything that’s not meant to be shared.
Embracing this knowledge I would like to introduce the concept of bridge modules. The bridge module is a container that only contains interfaces that exposes functionality. Its implementation is provided by the respective feature module. When we follow this bridge architecture for building modules, we can hide the internal implementation from other components.
In the above image: B only needs to know interface of A
- Split A into 2 modules: “A interface” & “A implementation”
- Inject “A Implementation” into B by “A Interface”
We are going to have a look into parts of the sample application, that I would be sharing at the end of this story.
In the below code snippet, I have created a UserDetailsNavigation in the user-bridge module. This is used for exposing navigation from one feature module to other modules. Feature-bridge modules are one of the robust ways of isolating a complex implementation so that other modules can use it.
In the user module, we would be implementing the interface. We would be using dagger as a dependency injection library in the example. User module would provide the implementation of the interface via Dagger Module.
This dagger module is been provided to App Module which is included in the App Component.
Other modules that want to use UserDetailsNavigation can now, inject it inside there feature modules. In the snippet below search-feature module wants to use UserDetailsNavigation. So we would just add as parameter whatever dependencies we require from the app component and dagger would provide its actual implementation. This dagger module is been provided to App Module which is included in the App Component.
Below is the dependencies build.gradle file for the search-feature module. We can see here it just required to know about the interface so user_bridge is included.
Benefits of modularization
- Build Time: With multiple independent modules, we can allow gradle to build multiple modules parallel which decreases the build time. Incremental builds would even faster as it only to build the module that has changed.
- Separation of Concerns: When we build modules we can hide inner working and expose only those part that is required for consumption. This way we can easily have encapsulation around our feature module.
- Scaling team: As the team scales engineer who is new to the codebase would not require to know all the complexity and contribute much faster with independent modules as they work only in isolated code. We can also have code ownership for each module, this enables us to know the point of contact.
If you want to check the sample example. You can check here:
Hopefully, you would have a better understanding of modularization, how to get started. There can be multiple ways to approach modularization choose whatever works for you.
If you like the article, remember to share it with the android community. Happy Coding!
Thank you Android weekly for selecting this article.