I have a Dagger object graph that is built from many smaller object graphs by using component dependency. Each component is essentially its own object graph and it exposes a few objects up to satisfy the dependencies of other components up the tree. The objects graphs within each component are irrelevant (so I'm not talking about Dagger @Modules
at all.)
@Component(dependencies = [RepositoryComponent::class])
interface AppComponent {
(Similar setup for other components)
With vanilla Dagger, it is up to me to create each component by calling their respective builder.
val appComponent = DaggerAppComponent
.builder()
.build()
(Usually in Application
class.)
But I also need to supply the component dependency by creating a Factory
and adding to its create
method
@Component(dependencies = [RepositoryComponent::class])
interface AppComponent {
@Component.Factory
interface Factory {
fun create(repositoryComponent: RepositoryComponent): AppComponent
}
}
and then calling the factory's create
method instead of the builder
val appcComponent = DaggerAppComponent
.factory()
.create(repositoryComponent = ...)
All of this is standard component dependency, more or less straight from the Dagger docs
Things get tough when you realize you have to reproduce the whole thing for RepositoryComponent
, ApiComponent
and DatabaseComponent
. The final "component creation" starts to balloon:
val apiComponent = ApiComponent.Factory
.create()
val databaseComponent = DatabaseComponent.Factory
.create()
val repositoryComponent = RepositoryComponent.Factory
.create(
apiComponent,
databaseComponent
)
val appComponent = AppComponent.Factory
.create(repositoryComponent)
}
You can imagine this won't scale once my app is 100 Dagger components. My application knows about the entire component tree. I'm leaking api
code up to the app
layer. Is there anyway to reduce this boiler plate? How do other teams handle this with larger apps?
First off, that's a lot of components. Between the repetition of boilerplate and the inability to remove unused bindings, you might want to double-check that you're not losing out on major savings by changing to a structure with significantly fewer top-level components (as opposed to subcomponents).
Assuming you want the independence of components, by some definition the complexity is not reducible: You may or may not want your DatabaseComponent
reused between repositories, for instance. When you start to need to express complex dependencies with scoping concerns, that starts to sound like the job that Dagger was meant to solve: You could have a top-level ComponentComponent with the sole responsibility to scope and instantiate the dependencies for your AppComponent.
Finally, I'm not sure I agree with your concern here:
My application knows about the entire component tree. I'm leaking api code up to the app layer.
Though it is important to encapsulate the separate parts of your app, we can also assume that some component's sole responsibility should be to configure your application and wire together its separate components. Instantiating this at the top level ("app level") doesn't seem like a failure of design to me, provided that you abide by the abstraction that your ApiComponent represents. In that way, other layers like your RepositoryComponent do not mind how your ApiComponent is implemented, where it is wired, or how it resolves. Your alternative is to lose out on the dependency injection such that RepositoryComponent creates its own ApiComponent, but if you want all of your objects to share an ApiComponent then you'll have to wire it up somewhere. As long as you keep other concerns from leaking into your component-wiring function, your current implementation seems as reasonable as any.