Getting Started
Add Kontainer to your project and wire up your first services.
Prerequisites
- JDK 17+
- Gradle with Kotlin JVM or multiplatform plugin
1. Add the dependency
In your build.gradle.kts:
dependencies {
implementation("io.peekandpoke.ultra:kontainer:0.107.2")
} Find the latest version on Maven Central.
2. Define a blueprint
A blueprint is an immutable description of all your services and their lifecycles. You create it once, then use it to stamp out container instances.
import io.peekandpoke.ultra.kontainer.kontainer
// Define some services
class Greeter {
fun sayHello() = "Hello!"
}
// Create a blueprint
val blueprint = kontainer {
singleton(Greeter::class)
} 3. Create a container and use it
// Create a container from the blueprint
val kontainer = blueprint.create()
// Retrieve a service
val greeter = kontainer.get(Greeter::class)
println(greeter.sayHello()) // "Hello!" 4. Add dependencies between services
Services declare their dependencies in the constructor. Kontainer resolves them automatically.
class Counter {
private var count = 0
fun next() = ++count
}
class MyService(val counter: Counter)
val blueprint = kontainer {
singleton(Counter::class)
singleton(MyService::class)
}
val kontainer = blueprint.create()
val service = kontainer.get(MyService::class)
println(service.counter.next()) // 1
println(service.counter.next()) // 2 Kontainer inspects the primary constructor of MyService, sees it needs a Counter, and injects it. No annotations needed.
5. Use factory methods when constructors aren't enough
Sometimes a service needs parameters the container doesn't know about. Use a factory lambda:
class MyService(private val counter: Counter, private val offset: Int) {
fun next() = counter.next() + offset
}
val blueprint = kontainer {
singleton(Counter::class)
// Factory: Kontainer injects Counter, you provide the offset
singleton(MyService::class) { counter: Counter ->
MyService(counter, 100)
}
} What happens on first use?
The first time you call blueprint.create(), Kontainer validates the entire dependency graph:
- Are all required dependencies registered?
- Are there ambiguous services (multiple implementations for one type)?
- Can all constructors be satisfied?
If anything is wrong, you get a KontainerInconsistent exception with a clear message listing every problem — not a cryptic runtime error later.