Global & Configurable Kodein DI

Introduction

The Configurable DI Plugin gives you :

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

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

The Configurable DI 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>7.1.0</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:7.1.0'
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:7.1.0'
Do not remove the kodein-generic-js (or kodein-erased-js) dependency. Both dependencies must be declared.

Native

ConfigurableDI

Configuring

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

Example: adding a module inside the global DI
fun test() {
    val di = ConfigurableDI()

    di.addModule(aModule)
    di.addExtend(otherDI)

    di.addConfig {
        bind<Dice>() with provider { RandomDice(0, 5) }
        bind<DataSource>() with singleton { SqliteDS.open("path/to/file") }
    }
}
The DI 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 ConfigurableDI object like any DI object.

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

Mutating

A ConfigurableDI can be mutable.

Example: Creating a mutable ConfigurableDI
val di = ConfigurableDI(mutable = true)

Using a mutable ConfigurableDI can lead to very bad code practice and very difficult bugs.
Therefore, using a mutable ConfigurableDI IS discouraged.
Note that every time a ConfigurableDI 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 ConfigurableDI can be configured even after first retrieval.

Example: mutating a mutable ConfigurableDI
fun test() {
    val di = ConfigurableDI(mutable = true)

    di.addModule(aModule)

    val ds: DataSource by di.instance()

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

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

The god complex: One True DI

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

For these cases, the di-conf module proposes a static DI.global instance.

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

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

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

Being globally aware

You can use the GlobalDIAware interface that needs no implementation to be aware of the global di.

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