Modularization of Android Applications based on Clean Architecture

Ahmad Efati
5 min readMay 23, 2021

Introduction

As a software engineer working on the smart phone application industry, I felt the lack of a practical example of Android application modular with regard to the Clean Architecture approach. Another significant characteristic of this article , in addition to providing a practical example, is to present the challenges I’ve faced with and their solutions.

For the ones in hurry, this is the complete code of the project:

Table of contents

  • Brief into to Clean Architecture and Modularization
  • Matching clean architectural layer with practical project modules
  • Structure of modules
  • Package files and classes in separate modules
  • Inverse relationship
  • How to switch between feature modules

Brief into to Clean Architecture and Modularization

Architecture means the overall design of the project. It’s the organization of the code into classes or files or components or modules. And it’s how all these groups of code relate to each other. The architecture defines where the application performs its core functionality and how that functionality interacts with things like the database and the user interface.

Clean Architecture have three primary layer (we could have more layers). Each layer has a distinct set of responsibilities:

  • Presentation layer - presents data to a screen and handle user interactions. For example, in this article, features, resources and views are in this layer
  • Domain layer - contains business logic
  • Data layer - manages application data eg. retrieve data from the network, manage data cache, Room library

In this paper, each of these layers is packaged in one or more modules. In addition to these layers, related files and classes are packaged in separate modules. The question that arises here is why do we use modularize? Modularizing the application can provides some advantages for user that they are :

  • more efficient builds
  • more manageable code
  • cleaner code
  • more Testability
  • more Scalability
  • applying SOLID
  • make easier branch management
  • make easier refactoring and etc

Now we will face two challenges: first, which file and class should be placed in each module, in other words, which files and classes should we decide to separate in multiple modules?. The 2nd is the challenge of how to relate modules to each other. You should avoid creating loops in the module graph.

Matching clean architectural layers with practical project modules

Now let’s see where do the modules that I mentioned earlier belong? In other words, I’ve been mapping between modules provided in the article and clean architecture layers.

Presentation layer:

  • Feature modules. for example home_module
  • common-databinding module
  • common-ui-res module
  • common-ui-view module

Domain layer:

  • domain module

Data layer:

  • data module

structure of modules

I’ve divided project to some modules. Well as you can probably guess I will give a brief explanation about each module:

App: The main task of this module is to link together features modules. It also gives the output of APK. Remember that App module using the application plugin in build.gradle file to signify this:

apply plugin: 'com.android.application'

Another important thing that it denotes is that all modules must be implemented in the `App`.

View(Features): Each feature contains a number of related fragments, layouts, viewModel, presentation layer models and a navigation graph that navigation graph of App need to include the graphs of our features Because App module doesn’t detects the feature modules, but the feature modules detect the App module.

navigation graph of App module (fig1)

common-ui-view: This module contains code which are shared between feature modules. For example, there are BaseViewModel, BaseFragment, AutoClearedValue and so on

common-databinding: This module contains BindingAdapter and Base Adapter for RecyclerView

common-ui-res: This module contains resources which are shared between feature modules. In this way, we are able to reuse the resources, layouts, and components in different modules, without need to duplicate code.

Data: The data module includes RoomDatabase, Dao, dataSource, Entity, Retrofite Api, general models. As previously mentioned this module is equivalent to the same data layers in clean architecture that only the domain layer has access to and the presentation layer do not have access to. To clarify, the bridge between the presentation layer and the data layer is the domain layer.

Base: files used in both the View module (Features) and other modules are included in this module. In fact, it is a general module that other modules have access to.

According to the figure below(Graph Modules), dotted lines are compiled by using implementation and normal lines are compiled by using api . Well as you see, base module is compiled by using api then If any change is implemented inside base module, gradle needs to recompile base, data, domain and features modules. in other words all modules which import data module can use base module. According to clean architecture, the presentation layer has access to the domain layer and the domain layer has access to the data layer but presentation layer do not have access to data layer.

Graph Modules(fig2)

Inverse Relationship

A challenge we face is that if the data layer to the domain layer and the domain layer has access to the presentation layer, then a cycle is created. Inverse Relationship is used to solve this issue.

You may be wondering what Inverse Relationship is? That’s why I used functions called mapper .The mapper function is used to breaking the cycle. An important point is that the mapper function of Data To Domain is located inside the domain layer because the domain layer has access to the data layer ( figure4). Similarly, the mapper function of UI To Domain is located inside the UI layer.

Inverse Relationship (fig3)
inverse relationship from data to domain layer fig(4)

How to switch between Feature modules

Feature modules never directly depend on each other. To communicate with each other, they use Deeplink. Each feature contains a navigation graph. We create a navigation graph in each feature module and include that navigation graph in the App navigation Graph. Because the App doesn’t detect the feature modules, but the feature modules know the App.

Here’s a visual summary over the project.

Final thoughts

Here’s the key takeaways from the article:

  • Benefits of clean architecture
  • How to implement clean architecture and modularization on android project
  • How to create graph module without cycle
  • How to move in the opposite direction of the graph module

--

--