Dagger2 for Modular Architecture

When designing a modular architecture or an Instant App the vertical dependencies can have only one direction: Feature modules depends on Base module, never viceversa.
In this article we will see how before forcing a modular architecture we had a dependency from the Base module to a feature module and how to break this dependency.

You can refer to the following linked article for a better explanation of the problem and what benefits we can get from a Modular Architecture.

Content Table

Dagger2 Introduction

Dagger 2 is a compile-time evolution approach to dependency injection.

Taking the approach started in Dagger 1.x to its ultimate conclusion, Dagger 2.x eliminates all reflection, and improves code clarity by removing the traditional ObjectGraph/Injector in favor of user-specified @Component interfaces.

This article will not talk about generic Dependency Injection, but will focus on implementation details for Dagger2 in Android.

A simple component

Image for post
Image for post
simple dagger2 ApplicationComponent

This is how looks a simple dagger ApplicationComponentexposing some dependencies like a UserRepository. Let’s see in detail:

  • ApplicationComponent : a dagger component exposing some dependencies instances. This usually needs an Application reference to build some dependencies like a SharedPrefs
  • NetworkModule : a dagger module that provides — create or build — dependencies to the component or dependency graph. Dagger2 will actually create the DI graph based on these modules. For example to build the UserRepository it needs a SharedPref as specified in the provide method. So provideSharedPref() will be executed and the result will feed provideUserRepository(sharedPref) to provide a UserRepository
  • Application: a custom android application that usually build the dagger ApplicationComponent passing a reference to itself

A simple component — Code

Let’s take a look to the code. Each component is annotated with @Component and list all the related modules. Each module is annotated with @Module and each provide method is annotated with @Provides .

Dagger2 will generate at build timeDaggerApplicationComponent that we can build and store to inject later an object or create a Subcomponent that we’ll see later.

SubComponents

Let’s add two activities representing two features: a RecipeBrowserActivity and a LoginActivity. We want two different components extending the ApplicationComponent with new dependencies. We can do this with dagger SubComponent creating a BrowserSubComponent and a LoginSubComponent .

A SubComponent is almost like a Component, it will have access to the Dependency Graph of ApplicationComponent and not only the dependencies exposed explicitly by ApplicationComponent.

For example: BrowserSubComponent needs access to provideOkHttp in ApplicationComponent to build the BrowserService .

Image for post
Image for post
ApplicationComponent and feature SubComponents

BrowserSubComponents — Code

Image for post
Image for post
Feature SubComponent in detail

Issue in a Modular Architecture

Image for post
Image for post
In a Modular Architecture or Instant app the BaseModule can’t access classes in Feature modules

Looking closely to the class diagram we can see that ApplicationComponent has a reference to BrowserSubComponent.Builder. In a modular architecture, where the ApplicationComponent belongs to the BaseModule and a SubComponent belongs to a feature module, this can’t compile.
The BaseModule doesn’t depend from feature modules, but the opposite. So ApplicationComponent can’t access a Builder class inside a feature module.

We have to revert the direction on this reference — red arrows in the diagram.
How do we do this? Using plain Components for feature modules.

Component dependency

Image for post
Image for post
Using plain component rather than SubComponent for feature modules

We can’t use anymore subcomponents, but we have to use components with a dependency to the ApplictionComponent. The builders of the features components will have a reference to the ApplicationComponent , and will be able to access exposed dependencies.

Component dependency — dependency access

BrowserComponent has access to the dependencies exposed explicitly in the ApplicationComponent but not the entire dependency graph— i.e. SharedPref but not okHttp as the orange arrow in the next picture.

Image for post
Image for post
okHttp can’t be accessed by BrowserComponent

That means we need to explicitly declare each dependency the BrowserComponent needs to access from the ApplicationComponent .

Image for post
Image for post
okHttp exposed explicitly so BrowserComponent can access it

Component dependency — code

Dependency Graph — Components split

While your app keep growing, the ApplicationComponent and its modules will keep growing with dependencies shared between features, and soon could become unmaintainable.
If your ApplicationComponent’s modules have fifty dependencies, you will end having fifty dependencies declared in the same interface/file. This could lead to many merging conflicts in a big team that will drain time from the real development.

A solution could be to create a subcomponent for each module of ApplicationComponent and then create a facade BaseSubComponent extending all this subcomponents.

Image for post
Image for post
ApplicationComponent split in multiple SubComponents

Having all the feature components extending a BaseSubComponent could generate some boilerplate code, so could be useful to create a BaseInjector extending AndroidInjector that will take care to get an instance of the BaseSubComponent and provide it during the generation of a feature component.

Take a look to BrowserComponent.kt to see how simple is to create a new feature component extending the BaseSubComponent.

Example Repository

Luigi Papino

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store