Modularization. Part 2. Dagger structure

Svyatoslav Chatchenko
2 min readOct 12, 2017

--

This series consist of

The main question is how to organize dependency injection between different application modules.
Dagger provides two mechanisms for building relations between components components (see Component relationships from JavaDoc). Subcomponents and component dependencies.

Subcomponents

We cannot use subcomponents because they are tightly coupled with parents (parent component should know the complete list of subcomponents). In other words, this is the circular dependency between application modules. That contradicts to our initial requirements.

Another disadvantage

the subcomponent implementation inherit the entire binding graph from its parent

This opens possibility to easily “leak” dependency. I prefer to be more explicit about what dependencies should be visible, even if it means writing more code.

But subcomponents can be good choice when are used within same application module.

Component dependencies

In that case, parent component knows nothing about dependent components. Also, we have to explicitly list all classes that we want to be available for dependent components. Let’s see an example. base-app app-module has LoggedOutComponent which provides UserRepository and know nothing about who will use it.

LoginComponent, from login app-module, depends on LoggedOutComponent. This allows to inject UserRepository into LoginPreseneter.

How to get reference to base component

LoggedOutComponent resides in base-app module which is the bottom level module. login module depends on it. Actual instance for LoggedOutComponent is created in App class inside app top-level module. We don’t want to introduce the dependency from login module to app module. Otherwise, it would mean circular dependency.

In order to mitigate that, base-app module defines interface LoggedOutComponentProvider

Application has to implement this interface. That allows to get easily access component by casting context.getApplicationContext to LoggedOutComponentProvider. That's even easier to do with extension function

and use it inside LoginActivity

This approach has one disadvantage — absence of compile time verification. Developer can forget to implement LoggedOutComponentProvider and notice that only as runtime error. Fortunately, we can mitigate this disadvantage by having instrumented tests.

Eliminate circular dependency is much more important.

Scopes

“Component dependencies” is very powerful technique. It allows composing different components into one. We could write something like that

Unfortunately, this has limitation. Only one component, from “dependencies array” can have a scope. All other should be without scope. Example above will work, if LoggedOutComponent has @Singleton scope and LoggedInComponent is without scope. But won’t work, if LoggedInComponent has a scope (@Singleton or any other).

That’s why we in this repo we introduce LoggedInComponent which is subcomponent of LoggedOutComponent. It provides the same UserRepository and UserName (which make sense only when the user is logged in).

Summary

  • Use “component dependency” for Dagger component’s relation between app-modules.
  • Break circular dependencies by introducing “component provider” contract
  • SubComponent can be used only within same app-module, because the force circular dependency

Project can be found on Github. Continue reading with Part 3 — Compilation time.

--

--