Dependency Injection
Don’t waste time and energy manually wiring your classes together. Let Koin create and wire everything instead!
Koin is a lightweight, Kotlin-native dependency injection library integrated into the Kairo ecosystem.
Why Koin?
Section titled “Why Koin?”-
Kotlin-first with compile-time generation:
- No proxies, simple Kotlin DSL.
- Dynamic modules (if you use them) are generated at compile time. No reflection!
-
Kairo-integrated:
- Uses Kairo lifecycle hooks.
- Your custom Features can easily provide bindings.
-
Easy testing: Koin + JUnit parameter resolvers let you add parameters to your integration test functions.
Installation
Section titled “Installation”Install kairo-dependency-injection-feature.
You don’t need to install Koin separately —
it’s included by default.
If you want to use Koin annotations (recommended), also add the relevant KSP lines from the snippet below.
plugins { id("com.google.devtools.ksp")}
dependencies { ksp("io.insert-koin:koin-ksp-compiler") implementation("software.airborne.kairo:kairo-dependency-injection-feature")}First, add the Feature to your Server.
val koinApplication = koinApplication()
val features = listOf( DependencyInjectionFeature(koinApplication),)Refer to Koin’s documentation for advanced usage.
Using annotations (recommended)
Section titled “Using annotations (recommended)”Bindings are created by annotating classes with @Single or @Factory.
@Singleclass UserStore( private val database: R2dbcDatabase, // Injected automatically.) { suspend fun get(id: UserId): UserModel? = suspendTransaction(db = database) { UserTable .selectAll() .where { UserTable.id eq id } .map(UserModel::fromRow) .singleNullOrThrow() }}@Singleclass UserService( private val userStore: UserStore, // Injected automatically.) { suspend fun get(id: UserId): UserModel? = userStore.get(id)}Constructor parameters are automatically injected.
Koin’s KSP-based annotation processor will pick up on these bindings, you just need to register them with your Feature.
@org.koin.core.annotation.Module@org.koin.core.annotation.ComponentScanclass UserFeature : Feature(), HasKoinModules { override val name: String = "User"
// Generated by KSP. override val koinModules: List<Module> = listOf(module)}Wiring it manually instead
Section titled “Wiring it manually instead”Instead of using annotations and KSP codegen, you can wire it up manually if you prefer.
class UserFeature( private val koin: Koin,) : Feature(), HasKoinModules { override val name: String = "User"
override val koinModules: List<Module> = listOf( module { single<UserStore> { UserStore(...) } single<UserService> { UserService(...) } } )}