Koin is a pragmatic lightweight dependency injection framework for Kotlin developers, providing core dependency injection functionality for Kotlin Multiplatform projects.
—
The Koin DSL provides a declarative way to define dependency injection modules using Kotlin's DSL capabilities. Modules contain dependency definitions that specify how objects are created and managed.
fun module(createdAtStart: Boolean = false, moduleDeclaration: ModuleDeclaration): Module
typealias ModuleDeclaration = Module.() -> UnitThe module function creates a new Koin module. The createdAtStart parameter determines if all singleton instances in this module should be created eagerly when the module is loaded.
val myModule = module {
single<DatabaseService> { DatabaseServiceImpl() }
factory<UserRepository> { UserRepositoryImpl(get()) }
}
// Module with eager initialization
val eagerModule = module(createdAtStart = true) {
single<AppConfig> { AppConfigImpl() }
}Creates a single instance that is shared across the entire application lifecycle.
inline fun <reified T> Module.single(
qualifier: Qualifier? = null,
createdAtStart: Boolean = false,
noinline definition: Definition<T>
): KoinDefinition<T>Parameters:
qualifier: Optional qualifier to distinguish between multiple implementationscreatedAtStart: If true, creates the instance immediately when the module loadsdefinition: Factory function that creates the instanceCreates a new instance each time it's requested.
inline fun <reified T> Module.factory(
qualifier: Qualifier? = null,
noinline definition: Definition<T>
): KoinDefinition<T>Parameters:
qualifier: Optional qualifier to distinguish between multiple implementationsdefinition: Factory function that creates the instanceCreates instances that are tied to a specific scope lifecycle.
inline fun <reified T> ScopeDSL.scoped(
qualifier: Qualifier? = null,
noinline definition: Definition<T>
): KoinDefinition<T>Koin provides constructor-based DSL functions that offer a more direct way to declare dependencies using constructor references.
Creates a singleton instance using a constructor reference.
inline fun <reified R> Module.singleOf(
crossinline constructor: () -> R,
noinline options: DefinitionOptions<R>? = null
): KoinDefinition<R>
inline fun <reified R, reified T1> Module.singleOf(
crossinline constructor: (T1) -> R,
noinline options: DefinitionOptions<R>? = null
): KoinDefinition<R>
inline fun <reified R, reified T1, reified T2> Module.singleOf(
crossinline constructor: (T1, T2) -> R,
noinline options: DefinitionOptions<R>? = null
): KoinDefinition<R>
// Additional overloads for more parameters...Creates a factory instance using a constructor reference.
inline fun <reified R> Module.factoryOf(
crossinline constructor: () -> R,
noinline options: DefinitionOptions<R>? = null
): KoinDefinition<R>
inline fun <reified R, reified T1> Module.factoryOf(
crossinline constructor: (T1) -> R,
noinline options: DefinitionOptions<R>? = null
): KoinDefinition<R>
inline fun <reified R, reified T1, reified T2> Module.factoryOf(
crossinline constructor: (T1, T2) -> R,
noinline options: DefinitionOptions<R>? = null
): KoinDefinition<R>
// Additional overloads for more parameters...Creates a scoped instance using a constructor reference.
inline fun <reified R> ScopeDSL.scopedOf(
crossinline constructor: () -> R,
noinline options: DefinitionOptions<R>? = null
): KoinDefinition<R>
inline fun <reified R, reified T1> ScopeDSL.scopedOf(
crossinline constructor: (T1) -> R,
noinline options: DefinitionOptions<R>? = null
): KoinDefinition<R>
inline fun <reified R, reified T1, reified T2> ScopeDSL.scopedOf(
crossinline constructor: (T1, T2) -> R,
noinline options: DefinitionOptions<R>? = null
): KoinDefinition<R>
// Additional overloads for more parameters...val myModule = module {
// Using constructor references instead of lambdas
singleOf(::DatabaseServiceImpl)
factoryOf(::UserRepositoryImpl)
// With multiple dependencies
singleOf(::UserServiceImpl) // constructor takes DatabaseService, UserRepository
scope<UserSession> {
scopedOf(::SessionDataImpl)
scopedOf(::UserPreferencesImpl)
}
}
// Equivalent to:
val traditionalModule = module {
single<DatabaseServiceImpl> { DatabaseServiceImpl() }
factory<UserRepositoryImpl> { UserRepositoryImpl() }
single<UserServiceImpl> { UserServiceImpl(get(), get()) }
scope<UserSession> {
scoped<SessionDataImpl> { SessionDataImpl() }
scoped<UserPreferencesImpl> { UserPreferencesImpl() }
}
}typealias Definition<T> = Scope.(ParametersDefinition?) -> TThe definition function is a lambda that creates instances. It receives:
Scope: The current scope for resolving dependenciesParametersDefinition?: Optional parameters passed during injectionWithin definition functions, use get() to resolve dependencies:
val myModule = module {
single<Database> { DatabaseImpl() }
single<UserService> { UserServiceImpl(get<Database>()) }
// With qualifier
single<Cache>(named("user-cache")) { UserCacheImpl() }
single<UserRepository> {
UserRepositoryImpl(get<Database>(), get<Cache>(named("user-cache")))
}
}fun Module.scope(qualifier: Qualifier, scopeSet: ScopeDSL.() -> Unit)
inline fun <reified T> Module.scope(scopeSet: ScopeDSL.() -> Unit)Scopes group dependencies with a shared lifecycle:
val myModule = module {
scope<UserSession> {
scoped<SessionData> { SessionDataImpl() }
scoped<UserPreferences> { UserPreferencesImpl(get()) }
}
// Named scope
scope(named("http-client")) {
scoped<HttpClient> { HttpClientImpl() }
scoped<ApiService> { ApiServiceImpl(get()) }
}
}Qualifiers distinguish between multiple implementations of the same type.
interface Qualifier {
val value: String
}
class StringQualifier(override val value: String) : Qualifier
class TypeQualifier(val kclass: KClass<*>) : Qualifier
fun named(name: String): StringQualifierval myModule = module {
// Multiple implementations with qualifiers
single<Database>(named("primary")) { PrimaryDatabaseImpl() }
single<Database>(named("cache")) { CacheDatabaseImpl() }
// Type-based qualifier
single<Logger>(qualifier<ConsoleLogger>()) { ConsoleLoggerImpl() }
single<Logger>(qualifier<FileLogger>()) { FileLoggerImpl() }
}class KoinDefinition<T>(val module: Module, val beanDefinition: BeanDefinition<T>) {
inline fun <reified Type> bind(): KoinDefinition<T>
fun createdAtStart(): BeanDefinition<T>
}The KoinDefinition class allows chaining additional configuration:
val myModule = module {
single<UserServiceImpl> { UserServiceImpl(get()) }
.bind<UserService>() // Bind to interface
.bind<Auditable>() // Bind to additional interface
single<DatabaseService> { DatabaseServiceImpl() }
.createdAtStart() // Create eagerly
}fun Module.includes(vararg module: Module)
fun Module.includes(module: Collection<Module>)
operator fun Module.plus(module: Module): List<Module>
operator fun Module.plus(modules: List<Module>): List<Module>Combine modules for better organization:
val databaseModule = module {
single<Database> { DatabaseImpl() }
}
val serviceModule = module {
includes(databaseModule)
single<UserService> { UserServiceImpl(get()) }
}
// Using operators
val allModules = databaseModule + serviceModulePass parameters during injection:
val myModule = module {
factory<UserService> { params ->
val userId = params.get<String>()
UserServiceImpl(userId, get())
}
}
// Usage
val userService = get<UserService> { parametersOf("user123") }val myModule = module {
single<Logger> {
if (BuildConfig.DEBUG) {
ConsoleLogger()
} else {
FileLogger()
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-insert-koin--koin-core