Android Architecture š
A Journey Looking For The Perfect Design

The Android landscape has so changed over the years, especially those two last years (thanks to Kotlin & Jetpack), and thatās pretty a good thing.
Today, Google seems to finally give us a pretty clear direction of how we should start to design our Android apps.
In a few words: multi-modules & MVVM.
The purpose of this article is not to present in detail each concept (MVVM, Coroutines, LiveData, etcā¦), but rather to share with you an overview of what could be a robust and scalable architecture in Android.
Disclaimers: This will be a long journeyā¦āļø
Table of Contents
- 1ļøā£ Demonstration Project
- 2ļøā£ Multi-modules
- 3ļøā£ MVVM & Databinding
- 4ļøā£ Navigation Component
- 5ļøā£ Unit & Instrumented Tests
1. Demonstration Project š±

Because a picture (well actually a project) is worth a thousand words, I created a sample Android app to show you each part of this architecture:
- š¾ Github Repo
- š² Demo APK
I also tried to use the most recent and used libraries I could to have a realistic and complete sample:
- Coroutines for asynchronous programming
- Room for SQLite
- Retrofit for networking
- Koin for dependency injection
- Architecture Components (Navigation, ViewModel, Databinding, etcā¦)
- NetworkBoundResource (with Coroutines & Room support)
- Animations during fragment transitions (Shared Element & Custom).
2. Considering Multi-modules š
If youāre a senior Android developer, youāre surely used to monolithic Android applications, where all your classes are inside one module divided into multiple packages.
Unfortunately, this approach tends to lead to high build times, difficulties to manage code in large teams and impossibility to reuse code across apps.
š For more info about modularization, those articles rock:
- Modularizing Android Applications by Joe Birch
- Plaid modularization by Ben Weiss
- Modularization, why you should care by Jeroen Mols
- Intro to App Modularization and Gradle dependency management with Kotlin (buildSrc) by Mario Sanoguera de Lorenzo
- Android Documentation: Create an Android library.
In this part, I will show you how you could divide your app into multiple modules. This design is based on this diagram from Android documentation that I tried to respect as much as possible:

Translated into a multi-modules Android project, hereās how this could be represented:

Letās dive into each module compositionā¦ š
2.1. App module.
This module is responsible for almost nothing and is actually doing nearly nothing, except managing dependency injection and including the other modules.
Itās the entry point of the app.

Because this module is the central point of our app, it will contain all the other modules:
2.2 Data modules
Those modules are obviously about data and the way to retrieve it. Furthermore, each module is responsible for the creation of its objects by creating a DI Module (this āmoduleā term is about dependency injection, donāt be confusedā¦ š ), used by the dependency injection framework (in this example, Koin).
In practice, those ādata modulesā are placed by convention into a data
folder. You will find for instance a remote module
, local module
and repository module
.

As you may have noticed, the thing pretty cool with modules is that you can easily link them with Gradle (and so reuse them !).
2.3 Feature modules
Those modules represent your features (basically each screen of your app). Located by convention into the features
folder, each feature module
is totally independent and is essentially composed of:
- One or multiple controllers (Fragment/Activity).
- A single ViewModel for each controller.

Such as data modules
, each feature module
is responsible for creating its dependencies through a DI Module :
Finally, a feature module
only has to include the repository module
inside its Gradle file in order to access some data.
2.4 Navigation module
Thatās the tough part, especially when you are in a multi-modules environment. Why? Because at one point, you will have to navigate between feature modules
.
Feature modules
are independents, and must NOT know about each other (neither aboutapp module
).
As far as I know, there is no official way to handle this properly. Instead, there are multiple and clever approaches to navigation in a multi-modules environment as you can see here or here (essentially based on Deep Link).
Personally, I choose the Navigation Component to handle this (I talk fully about it further in this article). In this way, the navigation module
is only responsible to handle navigation in the application thanks to a powerful system of navigation graphs.

In addition, navigation module
depends on no other module, but is always included in feature modules
.

However, note that this approach leads to some āacceptableā limitations as we will see later.
2.5 Common & CommonTest modules
Those last two modules contain some ācommonā classes potentially usable everywhere.
However, the common_test
module is only used through Unit & Instrumented tests (included with Gradle using testImplementation
and androidTestImplementation
).

Of course, you are totally free to completely change this approach of multi-modules and adapt it to your needsā¦ š
For your information, you can also find out other approaches about how developers are modularising their apps in this post.
3. Considering MVVM & Databinding š
Since Jetpack and the Architecture Components, a lot of things have changed in the Android ecosystem, in a good way.
Thanks to the ViewModel component, it became easy to store and manage UI-related data in a lifecycle conscious way. Also, the ViewModel
class allows data to survive configuration changes such as screen rotations! š
And of course, ViewModel brings us naturally to the MVVM architecture.
š For more info about MVVM, those resources rock:
- MVVM Google Sample by Jose AlcƩrreca
- MVVM by example by Husayn Hakeem
- Databinding Codelabs by Google
In practice, hereās how it looks like in a multi-modules Android project:

As you can see in this type of architecture, we tend to use the ācompositionā principle a lot in order to reuse each class more easily. That is one of the main reasons you need a dependency injection framework (like Koin or Dagger2) to handle this more properly.
3.1 MVVM: Model
The āModel Componentā represents the data and the business logic. Because we are in a multi-modules environment, itās pretty easy to include the āmodelsā we need (for example the repository module
).
Also, because we need sometimes to adapt/modify raw data fetched from the repository module
, itās a good practice to create a Use Case class between the Repository and the ViewModel. Thatās what Plaid app does for example.
The Use Case principle is a part of the Clean Architecture. I really loved this article that clearly talks about it š
Letās take for example the āHomeā feature module
.
As you can see, this āUse Caseā has a UserRepository injected directly in its constructor. Iām also using the invoke
operator function to make sure this use case contains only a single action.
Then, the previous Use Case will be used directly by the ViewModel of the feature.
3.2 MVVM: ViewModel
The āViewModelā component represents the heart of your feature/screen.
Ideally, when you look at the code inside a ViewModel, you should be able to understand and have a clear vision of :
- šŖ ALL the actions a user can make in your feature/screen
- š® What kind of data the view (UI) will show
Letās take a look at the āHomeā feature ViewModel :
As you can easily guess when reading this class, the āHomeā feature will :
- š® Show to the view a
Resource<List<User>>
through the publicusers
LiveData. In this way, UI will be able to observe the state of the data (loading, error or success) and the actual data (List<User>
) - šŖ Offer two actions to the user which use our app:
userClicksOnItem()
anduserRefreshesItems()
Note: In this sample, Iām using the MediatorLiveData to properly handle the refreshing of the users
LiveData (pull to refresh).
By the way, I highly recommend you to read this article to know whatās the best practices & anti-patterns when it comes to using a ViewModel.
3.3 MVVM: View
Through the MVVM architecture, the āViewā component has simply the responsibility to observe the LiveData and react to its changes.
In Android, we will use data binding to achieve this behavior. In this way, our controllers (Fragment/Activity) will be as light as possible!
Pretty light, isnāt it? As you may know with data binding, the most āinterestingā part is located within the XML layout :
Thanks to data binding, we basically tell the layout to use our ViewModel
and its LiveData
in order to print their data to the screen or proceed to some user actions.
If you need some custom binding behaviors, you can create some BindingAdapter
like this way :
Thanks to this approach, each concern regardless of UI is clearly separated. No need of findViewById
, Butterknife or Kotlin Android Extensions anymore (and it seems to be the Google point of view).
4. Considering Navigation Component āµļø
As I told you previously, navigation in an Android multi-modules environment is pretty tough and complicated.
Recently, Google released Navigation Component which really helps us to build complex navigation within our apps, as simply as it should be.
š For more info about Navigation Component, those resources rock:
- Google Codelabs
- Android Documentation
- Navigation Architecture Component for the Rest of Us by James Shvarts
In this way, I tried to find out how we could implement it in a multi-modules project. Android documentation seems to tell us that itās possible:
You should include the top-level graph in your main app module and have a Gradle reference for any modules containing navigation that you need to reference.
Why not! However, in practice, I didnāt want to centralize navigation in the app module
. So I chose to implement David VĆ”vraās approach and create a navigation module
that will handle all the navigation process of my app.
This solution comes with two āacceptable drawbacksā as explained pretty well by David :
1. Since ānavigationā module doesnāt have dependency on features, Fragment classes are red in Android Studio. You donāt have auto-complete for Fragment classes, but it compiles without any problems.
2. There is a bug which prevents generating intent filters for deep links. Itās already assigned, so hopefully it will be fixed in the final release.

Despite those drawbacks, we can perfectly use the Navigation Component inside our app and enjoy all its awesome features like:
- SafeArgs: The Navigation Architecture Component has a Gradle plugin called Safe Args that generates simple object and builder classes for type-safe access to arguments specified for destinations and actions.

- Navigation UI:
NavigationUI
contains methods that automatically update content in your top app bar as users navigate through your app. For example,NavigationUI
uses the destination labels from your navigation graph to keep the title of the top app bar up-to-date. Pretty cool to easily handle the āUp buttonā. - Easy testable: You will use
FragmentScenario
for testing the contents of your fragments in isolation. This allows you to verify a fragment's state and interactions in both unit and instrumentation tests.
FragmentScenario.
Extract from HomeInstrumentedTests.- Easy animations: The Navigation component lets you add both property and view animations to actions. As you may have seen in the demo project, it became so easy to add custom transitions or using Shared Element Transitions between destinations.
5. Considering Unit & Instrumented Tests š¦
In my opinion, a strong and scalable architecture should mean a full and easy testable environment. In a multi-modules architecture, testing becomes very accessible (because decoupled).
Letās take a look at the tests of data modules :
As you can see, each module (remote
, local
& repository
) are fully tested. Among the libraries I used, you will find Mockk (for mocking) and MockWebServer (to mock a fake web server), nothing more.
Each feature module is also fully tested (unit & instrumented tests)! Letās see what it looks like with the home feature
:
Those tests were actually pretty simple and swift to write, especially because each component is decoupled so it becomes really easy to properly isolate and test each one of themā¦ š
Proper testing is in my opinion, one of the most important parts when you want to design a scalable and performant application. However, sometimes, this part may be very hard, especially if your app is not properly designed and architectured.
Today, thanks to Architecture Components & Jetpack, building robust, testable, and maintainable Android app starts to become less painful and more supervised/standardized by Google.
If you are starting a new Android project, I highly recommend you to consider multi-modules and MVVM architecture. As you may have seen, itās really worth it.