Monolith and Modularization on Android

An Android application built as a monolith can be identified by having an app module on the top of the dependency chain. If your project has vertical layers as mentioned earlier, it’s a monolith. If it’s built with horizontal layers (feature-modules), and the app module on top has a dependency on all feature modules, it’s a monolith. Most of the applications will have one of these approaches. It’s the easiest way to set up your application, it allows for a straight-forward navigation pattern througout the application (as all parts of the app are known due to the dependency chain), and it makes sense from a conventional point of view.

Monolithic Dependency Graph

A non-monolithic Android application would look a bit different. There is no longer an app module on the top of the chain. There is no module that has god-like knowledge about all features, instead features are built in an independent way. The app is built on a need-to-know basis — which makes a lot of sense. Every feature module (or feature module-tree) only knows what it actually requires for its functionality.

Dependency Graph with Feature Satellites

Since the application will still run in a single process on the device, there is still the need to connect the dots and to combine all of these modules into an actual application. Features need to be able to navigate between each other, data has to be transferred between them, and backstacks have to be provided to ensure proper navigation throughout the process lifecycle (and beyond that, once the process has been killed and the app is recreated).

Splitting the application into feature satellites working on a need-to-know basis, one of the things we still have to think about is navigation. In a monolith navigating between features is pretty simple due to classes and features being known at compile-time. In a service-oriented architecture, this is not given, thus navigating by classes doesn’t work. An easy solution would be to rely on fully qualified class names ( com.brainasaservice.app.feature_service.MyActivity ) and rely on reflection — but that feels a bit dirty.

Thinking back to microservices, an implementation-agnostic way to navigate would be the way to go: Deep Linking. Just recently, I have published a post with more detailled and thorough thoughts on why deep linking is a good candidate for navigation in modular applications.

Deeplinks are supported natively by the system. They require no tight coupling between components, as feature satellites / services can simply register themselves as processors for certain schemes. Deep Links are capable of transmitting data, there is a well-defined scheme.

With the advancement of Android X and the architecture components, we also get access to the navigation component, which coincidentally also supports deep linking out of the box.