From Dagger2 to Kodein: A small experiment

Allan Yoshio Hasegawa
4 min readOct 3, 2017

Dagger2 can get a bit too verbose in some cases. For example, when you have a scope for each screen, then each screen will have to implement a Scope, a Module and a Component.

For Kotlin users there seems to be a promising alternative: Kodein.

After migrating from Dagger2 to Kodein on a toy project, here’s a list of observations:

Optional (skip it): You can check the full implementation with Kodein in this link. The project is from a previous blog post.

1) Kodein is not a dependency injection framework

While “Kodein” stands for KOtlin DEpendency INjection, Kodein is not really a dependency injection framework. Their docs describe it as a “dependency retrieval container”.

So, Kodein uses containers to pass dependencies around. How is that different from Dagger2? Let’s see a Kodein example taken from the docs:

val kodein = Kodein {
bind<Dice>() with provider { RandomDice(0, 5) }
bind<DataSource>() with singleton { SqliteDS.open("path/to/file") }
}
class Controller(private val kodein: Kodein) {
private val ds: DataSource = kodein.instance()
}

A container is created (first expression), then the container is passed to Controller; the job of retrieving the dependency is then delegated to the Controller. (See Section 4 for different ways of doing this).

With Dagger2 that example would look like this:

@Module
class Module {
@Provides fun providesDice(): Dice = RandomDice(0, 5)
@Provides fun providesDataSource(): DataSource = SqliteDS.open("path/to/file")
}
@Component
abstract class Component {
abstract fun getDice(): Dice
abstract fun getDataSource(): DataSource
}
class Controller @Inject constructor(val dataSource: DataSource) {}

There’s no “container” in Dagger2. Dependencies are injected via constructor, properties or methods. Controller has no idea from where dataSource is coming. This is a fundamental difference between Dagger2 and Kodein.

2) Kodein has a “very simple and readable declarative DSL”

This is taken from the docs; and I think the example above shows it. The code using Kodein is much more compact. That’s it :)

3) Open source projects using Kodein?

There are tons of materials online to learn Dagger2: tutorials, blog posts, open source projects; they are extremely useful.

For Kodein? Not much 😔 I still have so many questions on how to properly use Kodein.

4) Kodein has many ways to “inject” dependencies

We could have created the Controller from the first example like this:

val kodein = Kodein { ... }
class Controller(val dataSource: DataSource)

val controller = kodein.newInstance { Controller(instance()) }

Now Controller doesn’t depend on a container. This is closer to the Dagger2 approach of passing dependencies via constructor.

Or we could do something like this:

val kodein = Kodein { ... }
class Controller {
private val injector = KodeinInjector()
private val dataSource: DataSource by injector.instance()

init {
injector.inject(kodein)
// use dataSource
}
}

This resembles Dagger2 with injection via properties.

And, if you are “courageous” enough to abuse of reflection in your app, you can even use JSR330 with Kodein.

The point is, Kodein offers many ways to “inject” your dependencies. You don’t have to always pass a “container” to your objects.

5) Your classes will depend on Kodein, probably

On projects using Dagger2, your classes will depend on the annotations from the JSR330 standard. These classes don’t know about Dagger2.

On projects using Kodein, your classes might need to dependent on Kodein; it all hinge on which methods are used. In the first example the Controller depends on the Kodein container. In the “injector” example the Controller depends on the KodeinInjector.

6) Kodein evaluates dependencies at runtime

If you forget to bind an instance to a container, you’ll only get an error message during runtime. With Dagger2 you get an error at compile time.

7) Kodein container is too generic

In Dagger2 we declare modules and components with well defined methods. They can be used as contracts to know what instances can be accessed and by whom.

A Kodein container, however, doesn’t have any type information of what it provides or holds. This means that, for the type system, these two containers are the same:

val kodein = Kodein {
bind<Dice> with provider { RandomDice(0, 5) }
}
val foo = Kodein {
bind<UserRepository> with singleton { UserRepo() }
}

You can, accidentally, pass foo instead of kodein to an object. The project will compile successfully and it’ll break at runtime (if you’re lucky).

And?

I enjoyed using Kodein on this small project. I think its DSL is very simple, powerful and succinct. But I did run into problems that were only caught at runtime.

I’ll probably use Kodein again on small projects. For bigger projects I’d like to see how others are using Kodein first.

--

--