CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-insert-koin--koin-core

Koin is a pragmatic lightweight dependency injection framework for Kotlin developers, providing core dependency injection functionality for Kotlin Multiplatform projects.

Pending
Overview
Eval results
Files

scopes.mddocs/

Scoped Dependencies

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.

Scope Basics

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 = String

Scopes are containers for dependencies that share a common lifecycle. When a scope is closed, all its scoped instances are released.

Defining Scoped Dependencies

In Modules

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>
}

Usage Examples

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()) }
    }
}

Creating and Managing Scopes

Manual Scope Creation

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?

Example Usage

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()
        }
    }
}

KoinScopeComponent Integration

interface KoinScopeComponent : KoinComponent {
    val scope: Scope
}

Component Extension Functions

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?

Automatic Scope Management

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()
    }
}

Scope Lifecycle Patterns

Request Scope Pattern

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()
        }
    }
}

Feature Scope Pattern

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()
    }
}

Scope Linking and Hierarchy

Parent-Child Scopes

class ParentScope : KoinScopeComponent {
    override val scope: Scope = createScope()
    
    fun createChildScope(): Scope {
        return getKoin().createScope(
            scopeId = "child-${System.currentTimeMillis()}",
            qualifier = named("child"),
            source = this
        )
    }
}

Scope Dependencies

val hierarchicalModule = module {
    scope(named("parent")) {
        scoped<ParentService> { ParentServiceImpl() }
    }
    
    scope(named("child")) {
        scoped<ChildService> { 
            // Can access parent scope dependencies
            ChildServiceImpl(get<ParentService>()) 
        }
    }
}

Error Handling

class ScopeNotCreatedException(message: String) : RuntimeException(message)
class ClosedScopeException(message: String) : RuntimeException(message)

Safe Scope Operations

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
        }
    }
}

Best Practices

1. Always Close Scopes

// 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()
    }
}

2. Scope Naming

// 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"))

3. Lifecycle Alignment

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

docs

application.md

components.md

index.md

module-dsl.md

scopes.md

tile.json