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.
- Dagger2: Introduction
- A simple component
- Issue in a Modular Architecture
- Component dependency
- Component dependency — dependency access
- Dependency Graph — components split
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
Applicationreference to build some dependencies like a
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
UserRepositoryit needs a
SharedPrefas specified in the provide method. So
provideSharedPref()will be executed and the result will feed
provideUserRepository(sharedPref)to provide a
- Application: a custom android application that usually build the dagger
ApplicationComponentpassing 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
Dagger2 will generate at build time
DaggerApplicationComponent that we can build and store to inject later an object or create a Subcomponent that we’ll see later.
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
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
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.
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
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
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.