Koin: Thoughts on dynamic constructor dependency resolution

Created on 15 Mar 2018  路  2Comments  路  Source: InsertKoinIO/koin

What is the consensus on implementing dynamic constructor dependency resolution with an @Inject annotation on constructor parameters?

Taking inspiration from Dagger, perhaps in addition to standard factories we could define a class definition. Take the following example:

// current implementation
class MyViewModel(val repository: MyRepository)
class MyRepository()

We set up the dependency graph:

applicationContext {
        factory { MyRepository() }
        factory { MyViewModel(get()) }
}

And this works great! However, I've found that once a graph gets to any level of complexity, my graph becomes littered with factory { SomeRepository(get(), get(), get()) } which can become confusing, and I believe there is room for reducing duplicate code.

Instead, I envision something like this:

class MyViewModel(@Inject val repository: MyRepository)
class MyRepository()
// . . .

applicationContext{
    factory { MyRepository() }
    factory(MyViewModel::class.java)
}

and then upon koin's initialization, it would construct standard factory definitions by reading constructor annotation, and resolving those dependencies using get() under the hood. So reflection would only be required at initialization to create the factory definitions; after that, the definition is set in the same way any other definition is.

In my mind this would reduce boilerplate, consolidates work, and lets the user use Koin for what it's best at and not worry about implementing the same injection rules in two places. If I have to add a dependency to MyViewModel and it is already provided, I simply have to add a constructor parameter and Koin will resolve it for me (rather than needing to add another get() to the koin definitions and add the injection rule in both places).

Thoughts?

Most helpful comment

Hello @caleb-allen,

interesting subject.

the biggest challenge of Koin has been to avoid fall into reflect API and avoid the core features being polluted with costly API. The first times of Koin were hard because we needed to provide an interesting value without any use of @Inject nor reflection/proxy. A DSL first DI framework, in a light and pragmatic way.

Thing is done today. Now general design is somehow stable.

By using a reflect operator that find your constructor, you can block yourself from API such Property and Dynamic Parameters. That's a choice. Let's imagine an extension of the actual DSL:

bean<MyComponent>()

would be equivalent

bean{ MyComponent(get()) }

if we have:

class MyComponent(val dependency : Dependency)

I don't think, for now, that's a good idea to start using annotations, as we avoided it until now. The challenge is there: is it interesting to provide something to help you catch your constructor? Can we avoid use/think with annotations, as we put a DSL first framework?

The promise of Koin has also been to provide a way to declare your DI, and avoid as much possible to be linked to Koin API. All is done to help you make constructor injection. The Android world is the only area where it's not possible to make real DI, as we make service locator under cover.

All 2 comments

Hello @caleb-allen,

interesting subject.

the biggest challenge of Koin has been to avoid fall into reflect API and avoid the core features being polluted with costly API. The first times of Koin were hard because we needed to provide an interesting value without any use of @Inject nor reflection/proxy. A DSL first DI framework, in a light and pragmatic way.

Thing is done today. Now general design is somehow stable.

By using a reflect operator that find your constructor, you can block yourself from API such Property and Dynamic Parameters. That's a choice. Let's imagine an extension of the actual DSL:

bean<MyComponent>()

would be equivalent

bean{ MyComponent(get()) }

if we have:

class MyComponent(val dependency : Dependency)

I don't think, for now, that's a good idea to start using annotations, as we avoided it until now. The challenge is there: is it interesting to provide something to help you catch your constructor? Can we avoid use/think with annotations, as we put a DSL first framework?

The promise of Koin has also been to provide a way to declare your DI, and avoid as much possible to be linked to Koin API. All is done to help you make constructor injection. The Android world is the only area where it's not possible to make real DI, as we make service locator under cover.

I'll keep the idea for post 1.0 version.

For now, we are in "maintenance mode"

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sankarsana picture sankarsana  路  4Comments

erikhuizinga picture erikhuizinga  路  3Comments

dakuenjery picture dakuenjery  路  4Comments

TedHoryczun picture TedHoryczun  路  4Comments

miladsalimiiii picture miladsalimiiii  路  3Comments