docs/reference/koin-core/definitions.md
Definitions declare how Koin creates and manages your dependencies. This guide covers all definition types using both DSL and Annotations.
| Type | DSL | Annotation | Lifecycle | Use Case |
|---|---|---|---|---|
| Singleton | single() | @Singleton | One instance for app lifetime | Services, repositories, databases |
| Factory | factory() | @Factory | New instance each time | Presenters, use cases, stateful objects |
| Scoped | scoped() | @Scoped | One instance per scope | Activity-bound, session-bound objects |
| ViewModel | viewModel() | @KoinViewModel | Android ViewModel lifecycle | ViewModels |
import org.koin.plugin.module.dsl.*
val appModule = module {
// Singleton
single<Database>()
single<UserRepository>()
// Factory - new instance each time
factory<UserPresenter>()
// ViewModel
viewModel<UserViewModel>()
}
@Singleton // or @Single
class Database
@Singleton
class UserRepository(private val database: Database)
@Factory
class UserPresenter(private val repository: UserRepository)
@KoinViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel()
val appModule = module {
// With constructor reference (autowiring)
singleOf(::Database)
singleOf(::UserRepository)
factoryOf(::UserPresenter)
viewModelOf(::UserViewModel)
// With lambda (manual wiring)
single { Database() }
single { UserRepository(get()) }
factory { UserPresenter(get()) }
viewModel { UserViewModel(get()) }
}
| Concept | Compiler Plugin DSL | Classic DSL | Annotation |
|---|---|---|---|
| Singleton | single<MyClass>() | singleOf(::MyClass) | @Singleton / @Single |
| Factory | factory<MyClass>() | factoryOf(::MyClass) | @Factory |
| Scoped | scoped<MyClass>() | scopedOf(::MyClass) | @Scoped |
| ViewModel | viewModel<MyVM>() | viewModelOf(::MyVM) | @KoinViewModel |
| Worker | worker<MyWorker>() | workerOf(::MyWorker) | @KoinWorker |
:::info
The compiler plugin is analysing your class and function parameters, to generate the right call to Koin with get() function you don't have to write anymore.
:::
Creates one instance that's reused throughout the app:
// DSL
single<DatabaseHelper>()
// Annotation
@Singleton
class DatabaseHelper
Both create the same result - a single instance shared across all consumers.
Creates a new instance each time:
// DSL
factory<UserPresenter>()
// Annotation
@Factory
class UserPresenter(private val repository: UserRepository)
Creates one instance per scope:
// DSL
scope<MyActivity> {
scoped<ActivityPresenter>()
}
// Annotation
@Scoped(MyActivityScope::class)
class ActivityPresenter
Android ViewModel with proper lifecycle:
// DSL
viewModel<UserViewModel>()
// Annotation
@KoinViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel()
single<UserRepositoryImpl>() bind UserRepository::class
// Multiple bindings
single<MyServiceImpl>() binds arrayOf(ServiceA::class, ServiceB::class)
singleOf(::UserRepositoryImpl) bind UserRepository::class
// Or with lambda
single<UserRepository> { UserRepositoryImpl(get()) }
Interface binding is automatic when your class implements an interface:
@Singleton
class UserRepositoryImpl(
private val database: Database
) : UserRepository // Automatically binds to UserRepository
For explicit binding:
@Singleton
@Binds(UserRepository::class)
class UserRepositoryImpl : UserRepository
When you have multiple definitions of the same type. See also Injection with Qualifiers for retrieval.
With Compiler Plugin DSL, you need to annotate with @Named to use string qualifier (like you were previously using named())
@Named("local")
class LocalDatabase : Database
@Named("remote")
class RemoteDatabase : Database
class UserRepository(
@Named("local") private val localDb: Database,
@Named("remote") private val remoteDb: Database
)
single<LocalDatabase>()
single<RemoteDatabase>()
single<UserRepository>()
// Usage
val localDb: Database = get(named("local"))
single<Database>(named("local")) { LocalDatabase() }
single<Database>(named("remote")) { RemoteDatabase() }
// Usage
val localDb: Database = get(named("local"))
@Singleton
@Named("local")
class LocalDatabase : Database
@Singleton
@Named("remote")
class RemoteDatabase : Database
// In a consumer
@Singleton
class UserRepository(
@Named("local") private val localDb: Database,
@Named("remote") private val remoteDb: Database
)
Pass parameters at injection time:
Use the @InjectedParam to indicate that a parameter will be served by injected parameters.
class UserPresenter(
@InjectedParam userId : String,
repository : UserRepository
)
factory<UserPresenter>()
class UserPresenter(
userId : String,
repository : UserRepository
)
factory { params ->
UserPresenter(
userId = params.get(),
repository = get()
)
}
@Factory
class UserPresenter(
@InjectedParam val userId: String,
val repository: UserRepository // Auto-injected
)
// Usage
val presenter: UserPresenter = get { parametersOf("user123") }
class MyService(
val required: RequiredDep,
val optional: OptionalDep? // Resolved with getOrNull()
)
single<MyService>()
single {
MyService(
required = get(),
optional = getOrNull()
)
}
Nullable parameters are handled automatically:
@Singleton
class MyService(
val required: RequiredDep,
val optional: OptionalDep? // Resolved with getOrNull()
)
Defer instance creation:
class MyService(
val lazyDep: Lazy<HeavyDependency> // Deferred creation
)
single<MyService>()
single {
MyService(
lazyDep = inject() // Lazy<Dependency>
)
}
@Singleton
class MyService(
val lazyDep: Lazy<HeavyDependency> // Deferred creation
)
Inject configuration values:
class ApiClient(
@Property("api_url") val url: String,
@Property("api_key") val key: String
)
single<ApiClient>()
single {
ApiClient(
url = getProperty("api_url"),
key = getProperty("api_key", "default")
)
}
@Singleton
class ApiClient(
@Property("api_url") val url: String,
@Property("api_key") val key: String
)
Execute code when instance is released:
single {
Database()
} onClose {
it?.close() // Called when Koin stops or scope closes
}
Create instance eagerly at startup:
// Compiler Plugin DSL
single<ConfigManager>() withOptions {
createdAtStart()
}
// Classic DSL
single(createdAtStart = true) {
ConfigManager()
}
val prodModule = module {
single<ApiService> { ProductionApi() }
}
val testModule = module {
single<ApiService> { MockApi() } // Overrides production
}
startKoin {
modules(prodModule, testModule)
}
In strict mode, mark overrides explicitly:
val testModule = module {
single<ApiService> { MockApi() }.override()
}
startKoin {
allowOverride(false)
modules(prodModule, testModule)
}
single for stateless services - Repositories, clients, helpersfactory for stateful objects - Presenters, use cases with statescoped for lifecycle-bound objects - Activity, Fragment, Sessioncreate(::builder) for external libraries - Safer dependency resolution