Implementing Dependency Inversion using Dagger components

Fabio Collini
Google Developer Experts
7 min readJun 12, 2019

--

The SOLID principles were introduced for the first time by Robert C. Martin in the early 2000s. This paper explains the five principles even if the acronym SOLID is not used, it will be introduced later. It’s a long time ago but they are still valid today: they are the base of the Clean Architecture used in many Android applications. SOLID is an acronym: the D stands for Dependency Inversion, probably the most complicated (but also the most important) of the five principles.

The Dependency Inversion principle is usually defined in this way:

High level modules should not depend upon low level modules

The high level modules usually contain the business logic of the application, the low level modules the implementation details. For example an algorithm that calculates something is defined in an high level module while the database code is in a low level module.

It’s time to see how to apply this principle on a multi-module Android project and how to leverage Dagger to manage the dependencies organized in this way. Let’s start!

Dependency inversion in practice

Let’s see an example to explain how to use the Dependency Inversion. The WeatherUseCase class returns the weather conditions based on the device location:

It uses coroutines and other stuff and probably manages the exceptions in a bad way, the important point here is that two dependencies are declared in the constructor. So this class is using the dependency injection, is it using also the dependency inversion? The name is similar but the meaning is different.

To answer this question we need to see a higher level view of the example, the classes can be organized into three modules:

The domain module (in this post the word module always refers to an Android Studio module) contains just the WeatherUseCase class. It depends on the other two modules that define interfaces and implementations of the classes to get the device location and to retrieve the weather from a remote server. Looking at this diagram we can answer the previous question: this example uses the dependency injection but not the dependency inversion. The reason is that, contrary to the principle, the high level module domain depends upon the low level modules weather and location.

Dagger in a multi module project

Dagger can be configured in many ways in the previous example, for example using subcomponents, component dependencies or Dagger Android. Here we’ll use component dependencies, an approach similar to the one described in this post.

The Dagger code can be organized in two ways:

  • the Dagger code can be defined only in the top level module (usually the application)
  • each module can define its own Dagger configuration

The first approach seems clearer because, using it, most of the modules in the project are not aware of Dagger. Although it works well for small projects, when there are many classes to manage I prefer to define the Dagger configuration inside every module.

Going back to the previous example each module can define its own Dagger component, the components defined in weather and location are really simple:

The DomainComponent defined in the domain module uses the other two components as dependencies:

Here a diagram similar to the previous one to show the dependencies between the components:

WeatherUseCase constructor is then annotated using @Inject:

In this way Dagger can create a new instance of WeatherUseCase using the objects provided by LocationComponent and WeatherComponent as constructor arguments.

Let’s invert the dependencies!

How can we apply dependency inversion in this project? The goal is to invert the dependencies, weather and location should depend upon domain. The simplest way to achieve it is to move the interfaces LocationManager and TemperatureRepository to the domain module:

Now the dependencies between classes are the same but we can see that the module's dependencies are the opposite of the original example. The low level modules depend upon the high level module, so we are using Dependency Inversion.

The Dependency Inversion is largely used in the Clean Architecture, the main benefit is in common with this architecture:

the domain module is framework independent , it doesn’t contain any reference to the frameworks used for the implementation

Based on this consideration we have other two important benefits:

  • code convention Vs architecture: even without using the Dependency Inversion in many projects the domain doesn’t contain any reference to the Android classes and it can be tested using JVM tests. But probably in that project it’s just because of a code convention, developers know that shouldn’t use the Android classes on that module. Using the Dependency Inversion we are 100% sure that Android classes (or other low level framework classes) are not used there, they are not in the classpath so there is a compilation error in case they are used.
  • JVM tests are quick: the classpath is really small, it contains just a few dependencies to some utility libraries and no dependencies to other modules. So JVM tests on that module can be executed really fast, enabling TDD. And the domain contains the business logic, the code that deserves to be tested.

My talk at Droidcon Italy 2019 was about Clean Architecture and especially Dependency Inversion. If you are interested here there are the slides and the video of the talk.

Using Dagger with inverted dependencies

In the initial example the DomainComponent depended on the other two components, after inverting the dependencies this code doesn’t compile anymore because the domain module doesn’t depend on the other two modules.

Dependency Inversion can be used to fix this issue as well, we can remove the dependency on LocationComponent and WeatherComponent and replace them with a new interface DomainDependencies:

The DomainDependencies interface contains the objects needed byDomainComponent to create a new instance of WeatherUseCase. It’s important to notice that this new interface is not annotated using @Component, it’s a standard Kotlin interface with two properties (it works even defining two methods instead of the properties).

The DomainDependencies implementation must be defined in a module that depends on all the other modules, usually the application can be used to wire all components. A simple implementation can just delegate to the LocationComponent and WeatherComponent:

The diagram with the component's dependencies is a bit more complicated:

An example similar to this one is available on this post about Dagger on library development. The idea is similar but it’s used to manage something slightly different.

Component dependencies and scopes

The DomainDependencies implementation can be created also using a real Dagger component. We can just create an interface annotated with Component:

DomainDependenciesImpl knows how to create the objects defined in DomainDependencies thanks to the dependencies to the other two components. So no extra methods need to be defined, it just works!

EDIT: It works thanks to a feature introduced in Dagger 2.27. Using older versions a hacky solution is necessary (see 1225 for details).

In a multi module project, organized in a way similar to that described in this post, I think it’s a good idea to define a scoped component for each module. In this way each component can define its own singleton objects. The scope depends on where the components are stored, in this example all the components are saved in the Application.

The three Components defined uses its own scope annotation (for example LocationSingleton and WeatherSingleton). This is not required, it works even using the same annotation for each component. It’s important to notice that a custom annotation must be defined because a component scoped using the standardSingleton annotation cannot depend on other scoped components. However I think that defining multiple annotations the code is clearer and the error messages in case of errors are more explicit.

A real example of an application that can benefit from this organization is an app that uses OkHttp, Picasso, Retrofit and Apollo (let’s say that some features use a REST endpoint and other a GraphQL endpoint). In this example we can create four components, each component defines its own singleton (like the OkHttpClient or the ApolloClient). The OkHttp module is used by the other three modules to share the same OkHttpClient. We can achieve something similar using a single Dagger Component that defines all the singletons, however using multiple components there is a better separation of concerns and a feature module can include only the modules (and the libraries) that are needed. If a feature module uses only a GraphQL endpoint it can just ignore the module that defines the Retrofit stuff.

Wrapping up

Dependency Inversion is a principle that can help a lot to organize a big project in small independent modules. In this post we have seen an example on how to organize Dagger components on a project that apply the Dependency Inversion. The complete example is available on the dagger branch of this GitHub repository.

--

--

Fabio Collini
Google Developer Experts

Android GDE || Android engineer @nytimes || author @androidavanzato || blogger @codingjam