Koin is a pragmatic lightweight dependency injection framework for Kotlin developers, providing core dependency injection functionality for Kotlin Multiplatform projects.
—
Koin scopes provide lifecycle-aware dependency management, allowing you to create and manage dependencies that are tied to specific lifecycles like user sessions, request contexts, or feature workflows.
class Scope(
val scopeQualifier: Qualifier,
val id: ScopeID,
val isRoot: Boolean = false,
val _koin: Koin
) {
inline fun <reified T> get(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null
): T
inline fun <reified T> inject(
qualifier: Qualifier? = null,
mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(),
noinline parameters: ParametersDefinition? = null
): Lazy<T>
fun close()
}
typealias ScopeID = StringScopes are containers for dependencies that share a common lifecycle. When a scope is closed, all its scoped instances are released.
fun Module.scope(qualifier: Qualifier, scopeSet: ScopeDSL.() -> Unit)
inline fun <reified T> Module.scope(scopeSet: ScopeDSL.() -> Unit)
class ScopeDSL(val scopeQualifier: Qualifier, val module: Module) {
inline fun <reified T> scoped(
qualifier: Qualifier? = null,
noinline definition: Definition<T>
): KoinDefinition<T>
inline fun <reified T> factory(
qualifier: Qualifier? = null,
noinline definition: Definition<T>
): KoinDefinition<T>
}val userModule = module {
// Type-based scope
scope<UserSession> {
scoped<SessionData> { SessionDataImpl() }
scoped<UserPreferences> { UserPreferencesImpl(get()) }
scoped<ShoppingCart> { ShoppingCartImpl(get()) }
}
// Named scope
scope(named("request")) {
scoped<RequestContext> { RequestContextImpl() }
scoped<ValidationService> { ValidationServiceImpl(get()) }
}
}fun Koin.createScope(scopeId: ScopeID, qualifier: Qualifier, source: Any? = null): Scope
fun Koin.getOrCreateScope(scopeId: ScopeID, qualifier: Qualifier, source: Any? = null): Scope
fun Koin.getScope(scopeId: ScopeID): Scope
fun Koin.getScopeOrNull(scopeId: ScopeID): Scope?class RequestHandler : KoinComponent {
fun handleRequest(requestId: String) {
// Create a request scope
val requestScope = getKoin().createScope(
scopeId = "request-$requestId",
qualifier = named("request")
)
try {
// Use scoped dependencies
val context = requestScope.get<RequestContext>()
val validator = requestScope.get<ValidationService>()
// Process request...
} finally {
// Always close scope to clean up
requestScope.close()
}
}
}interface KoinScopeComponent : KoinComponent {
val scope: Scope
}fun <T : Any> T.getScopeId(): ScopeID
fun <T : Any> T.getScopeName(): TypeQualifier
fun <T : KoinScopeComponent> T.createScope(
scopeId: ScopeID = getScopeId(),
source: Any? = null,
scopeArchetype: TypeQualifier? = null
): Scope
fun <T : KoinScopeComponent> T.getScopeOrNull(): Scope?class UserSession : KoinScopeComponent {
override val scope: Scope = createScope()
// These will be resolved from the session scope
private val sessionData: SessionData by scope.inject()
private val preferences: UserPreferences by scope.inject()
private val cart: ShoppingCart by scope.inject()
fun login(userId: String) {
sessionData.userId = userId
preferences.load(userId)
}
fun logout() {
scope.close() // Cleans up all scoped instances
}
}
class ShoppingFeature : KoinScopeComponent {
override val scope: Scope = createScope()
private val cart: ShoppingCart by scope.inject()
private val paymentService: PaymentService by scope.inject()
fun addToCart(item: Item) {
cart.add(item)
}
fun checkout() {
val items = cart.getItems()
paymentService.processPayment(items)
}
fun cleanup() {
scope.close()
}
}val webModule = module {
scope(named("request")) {
scoped<RequestContext> { RequestContextImpl() }
scoped<UserSession> { UserSessionImpl(get()) }
scoped<DatabaseTransaction> { DatabaseTransactionImpl() }
}
}
class RequestProcessor : KoinComponent {
fun processRequest(request: HttpRequest): HttpResponse {
val scope = getKoin().createScope("req-${request.id}", named("request"))
return try {
val context = scope.get<RequestContext>()
val transaction = scope.get<DatabaseTransaction>()
// Process with scoped dependencies
handleRequest(request, context, transaction)
} finally {
scope.close()
}
}
}val featureModule = module {
scope<OrderWorkflow> {
scoped<OrderBuilder> { OrderBuilderImpl() }
scoped<InventoryChecker> { InventoryCheckerImpl(get()) }
scoped<PaymentProcessor> { PaymentProcessorImpl(get()) }
}
}
class OrderWorkflow : KoinScopeComponent {
override val scope: Scope = createScope()
private val orderBuilder: OrderBuilder by scope.inject()
private val inventoryChecker: InventoryChecker by scope.inject()
private val paymentProcessor: PaymentProcessor by scope.inject()
suspend fun processOrder(orderData: OrderData): OrderResult {
val order = orderBuilder.build(orderData)
val available = inventoryChecker.check(order.items)
return if (available) {
paymentProcessor.process(order)
} else {
OrderResult.OutOfStock
}
}
fun complete() {
scope.close()
}
}class ParentScope : KoinScopeComponent {
override val scope: Scope = createScope()
fun createChildScope(): Scope {
return getKoin().createScope(
scopeId = "child-${System.currentTimeMillis()}",
qualifier = named("child"),
source = this
)
}
}val hierarchicalModule = module {
scope(named("parent")) {
scoped<ParentService> { ParentServiceImpl() }
}
scope(named("child")) {
scoped<ChildService> {
// Can access parent scope dependencies
ChildServiceImpl(get<ParentService>())
}
}
}class ScopeNotCreatedException(message: String) : RuntimeException(message)
class ClosedScopeException(message: String) : RuntimeException(message)class SafeScopeHandler : KoinComponent {
fun safeGetFromScope(scopeId: ScopeID): UserService? {
return try {
val scope = getKoin().getScope(scopeId)
scope.get<UserService>()
} catch (e: ScopeNotCreatedException) {
null
} catch (e: ClosedScopeException) {
null
}
}
}// Good - using try-finally
val scope = getKoin().createScope("temp", named("temp"))
try {
val service = scope.get<TempService>()
service.doWork()
} finally {
scope.close()
}
// Better - using use-like pattern
inline fun <T> withScope(
scopeId: ScopeID,
qualifier: Qualifier,
block: (Scope) -> T
): T {
val scope = getKoin().createScope(scopeId, qualifier)
return try {
block(scope)
} finally {
scope.close()
}
}// Use descriptive scope IDs
val userScope = createScope("user-${userId}", named("user"))
val requestScope = createScope("request-${requestId}", named("request"))
val featureScope = createScope("feature-shopping-${sessionId}", named("shopping"))class ActivityController : KoinScopeComponent {
override val scope: Scope = createScope()
fun onCreate() {
// Scope is created, scoped dependencies available
}
fun onDestroy() {
scope.close() // Clean up when lifecycle ends
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-insert-koin--koin-core