Dagger2 for Modular Architecture

I hate Android
Feb 2, 2018 · 5 min read

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

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 .

BrowserSubComponents — Code

Issue in a Modular Architecture

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

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.

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

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.

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

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade