Global & Configurable Kodein DI

Introduction

The Configurable Kodein Plugin gives you :

  • A ConfigurableKodein class that you can pass around and have different sections of your code configure its bindings.

  • A Kodein.global instance that allows to have "one true" source.

The Configurable Kodein Plugin is an extension that is not proposed by default, this paradigm is in a separate module.

Using or not using this is a matter of taste and is neither recommended nor discouraged.

Install

JVM

With Maven

<dependency>
    <groupId>org.kodein.di</groupId>
    <artifactId>kodein-di-conf-jvm</artifactId>
    <version>6.5.5</version>
</dependency>
Do not remove the kodein-generic-jvm (or kodein-erased-jvm) dependency. Both dependencies must be declared.

With Gradle

compile 'org.kodein.di:kodein-di-conf-jvm:6.5.5'
Do not remove the kodein-generic-jvm (or kodein-erased-jvm) dependency. Both dependencies must be declared.

Javascript (Gradle)

compile 'org.kodein.di:kodein-di-conf-js:6.5.5'
Do not remove the kodein-generic-js (or kodein-erased-js) dependency. Both dependencies must be declared.

Native

ConfigurableKodein

Configuring

You can import modules, extend kodein objects, or add bindings inside this ConfigurableKodein using addImport, addExtend and addConfig.

Example: adding a module inside the global Kodein
fun test() {
    val kodein = ConfigurableKodein()

    kodein.addModule(aModule)
    kodein.addExtend(otherKodein)

    kodein.addConfig {
        bind<Dice>() with provider { RandomDice(0, 5) }
        bind<DataSource>() with singleton { SqliteDS.open("path/to/file") }
    }
}
The Kodein underlying instance will effectively be constructed on first retrieval. Once it is constructed, trying to configure it will throw an IllegalStateException.

Retrieving

You can use a ConfigurableKodein object like any Kodein object.

Once you have retrieved the first value with a ConfigurableKodein, trying to configure it will throw an IllegalStateException.

Mutating

A ConfigurableKodein can be mutable.

Example: Creating a mutable ConfigurableKodein
val kodein = ConfigurableKodein(mutable = true)

Using a mutable ConfigurableKodein can lead to very bad code practice and very difficult bugs.
Therefore, using a mutable ConfigurableKodein IS discouraged.
Note that every time a ConfigurableKodein is mutated, its cache is entirely flushed, meaning that it has a real impact on performance!
Please use the mutating feature only if you truly need it, know what you’re doing, and see no other way.

A mutable ConfigurableKodein can be configured even after first retrieval.

Example: mutating a mutable ConfigurableKodein
fun test() {
    val kodein = ConfigurableKodein(mutable = true)

    kodein.addModule(aModule)

    val ds: DataSource by kodein.instance()

    kodein.addModule(anotherModule) (1)
}
1 This would have failed if the ConfigurableKodein was not mutable.

You can also use the clear method to remove all bindings.

The god complex: One True Kodein

Sometimes, you want one static Kodein for your entire application. E.g. you don’t want to have to hold & pass a Kodein instance throughout your application.

For these cases, the kodein-conf module proposes a static Kodein.global instance.

Example creating, configuring and using the global one true Kodein.
fun test() {
    Kodein.global.addModule(apiModule)
    Kodein.global.addModule(dbModule)

    val ds: DataSource by Kodein.global.instance()
}

Just like any ConfigurableKodein, Kodein.global must be configured before being used for retrieval, or an IllegalStateException will be thrown. It is possible to set Kodein.global to be mutable by setting Kodein.global.mutable = true but it must be set before any retrieval!

Being globally aware

You can use the GlobalKodeinAware interface that needs no implementation to be aware of the global kodein.

Example: a KodeinGlobalAware class
class MyManager() : KodeinGlobalAware {
    val ds: DataSource by instance()
}