DI

The default dependency injection graph.

This supplements purely code-based DI solutions (which use by lazy instead of @Singleton etc.) by tracking the interdependencies between the DI nodes and re-creating sub-graphs whenever a DI object is replaced dynamically (e.g. because the app wants to reconfigure itself).

Example how to define a code-based DI graph:

// -------------
// module foo
// -------------

// Convenience accessor for FooDeps
val DIResolver.fooDeps: LazyProperty<FooDeps> get() = DI.run { get() }

// The actual FooDeps DI module/node. This is a circular dependency with BarDeps below.
class FooDeps(
val configFlag: Boolean,
lazyBarDeps: LazyProperty<BarDeps>,
) {
// All deps have to be resolved lazily
val barDeps by lazyBarDeps

// All deps have to be resolved lazily
val circularConfigFlag by lazy { barDeps.configFlag }

public fun myUseCase() = MyUseCase(circularConfigFlag)
}

class MyUseCase(val circularConfigFlag: Boolean)

// -------------
// module bar
// -------------

// Convenience accessor for BarDeps
val DIResolver.barDeps: LazyProperty<BarDeps> get() = DI.run { get() }

// Circular dependency to Foo, so we have
class BarDeps(lazyFooDeps: LazyProperty<FooDeps>) {
val fooDeps: FooDeps by lazyFooDeps

val configFlag: Boolean by lazy { fooDeps.configFlag }
}