Defining Services

Kontainer supports three service lifecycles — Singleton, Dynamic, and Prototype — plus existing instances and interface-based registration.

Singleton

A singleton is created once and shared across all container instances created from the same blueprint.

class Counter {
    private var count = 0
    fun next() = ++count
}

val blueprint = kontainer {
    singleton(Counter::class)
}

// Same instance across all containers from this blueprint
val k1 = blueprint.create()
val k2 = blueprint.create()

println(k1.get(Counter::class).next())  // 1
println(k2.get(Counter::class).next())  // 2  (same instance!)

Dynamic

A dynamic service is a singleton within a single container instance. Each new container gets its own fresh instance.

val blueprint = kontainer {
    dynamic(Counter::class)
}

val k1 = blueprint.create()
val k2 = blueprint.create()

println(k1.get(Counter::class).next())  // 1
println(k1.get(Counter::class).next())  // 2  (same instance within k1)
println(k2.get(Counter::class).next())  // 1  (fresh instance in k2)

Dynamic services are the key to per-request scoping.

Prototype

A prototype creates a new instance every time it is requested — whether retrieved directly or injected.

val blueprint = kontainer {
    prototype(Counter::class)
}

val kontainer = blueprint.create()

println(kontainer.get(Counter::class).next())  // 1
println(kontainer.get(Counter::class).next())  // 1  (new instance each time!)

Existing instances

Register an already-created object as a singleton:

object AppConfig {
    val dbUrl = "jdbc:postgresql://localhost/mydb"
}

val blueprint = kontainer {
    instance(AppConfig)
}

Hiding implementations behind interfaces

Register a service under its interface so consumers never see the concrete class:

interface GreeterInterface {
    fun sayHello(): String
}

class Greeter : GreeterInterface {
    override fun sayHello() = "Hello!"
}

val blueprint = kontainer {
    // Registered as GreeterInterface — Greeter is hidden
    singleton(GreeterInterface::class, Greeter::class)
}

val kontainer = blueprint.create()

kontainer.get(GreeterInterface::class)  // works
kontainer.get(Greeter::class)           // throws ServiceNotFound

This pattern works with all lifecycle types: singleton, dynamic, prototype, instance, and factory methods.