CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-kodein-di--kodein-di

KOtlin DEpendency INjection - A straightforward and yet very useful dependency retrieval container for Kotlin Multiplatform

Pending
Overview
Eval results
Files

scoping-and-context.mddocs/

Scoping and Context

Context-aware dependency management with support for hierarchical scopes, context translation, and lifecycle management for fine-grained control over dependency lifecycles and sharing.

Capabilities

DIContext System

Core context system for providing scoped dependency resolution with type-safe context management.

/**
 * Context definition with type and value for scoped dependency resolution
 * @param C Type of the context value
 */
interface DIContext<C : Any> {
    /** TypeToken representing the context type */
    val type: TypeToken<in C>
    
    /** The actual context value */
    val value: C
    
    /**
     * Immediate context implementation with direct value
     * @param type TypeToken for the context type
     * @param value The context value
     */
    data class Value<C : Any>(
        override val type: TypeToken<in C>,
        override val value: C
    ) : DIContext<C>
    
    /**
     * Lazy context implementation with deferred value evaluation
     * @param type TypeToken for the context type
     * @param getValue Function that provides the context value when needed
     */
    class Lazy<C : Any>(
        override val type: TypeToken<in C>,
        val getValue: () -> C
    ) : DIContext<C> {
        override val value: C by lazy(getValue)
    }
    
    companion object {
        /**
         * Create a context with immediate value
         * @param type TypeToken for the context type
         * @param value The context value
         * @return DIContext instance
         */
        operator fun <C : Any> invoke(type: TypeToken<in C>, value: C): DIContext<C>
        
        /**
         * Create a context with lazy value evaluation
         * @param type TypeToken for the context type
         * @param getValue Function that provides the context value
         * @return DIContext instance with lazy evaluation
         */
        operator fun <C : Any> invoke(type: TypeToken<in C>, getValue: () -> C): DIContext<C>
    }
}

/**
 * Create a DIContext from a typed value
 * @param context The context value
 * @return DIContext wrapping the value
 */
fun <C : Any> diContext(context: C): DIContext<C>

/**
 * Create a DIContext with lazy evaluation
 * @param getContext Function that provides the context value
 * @return DIContext with lazy evaluation
 */
fun <C : Any> diContext(getContext: () -> C): DIContext<C>

Scope System

Comprehensive scoping system for managing instance lifecycles and sharing patterns across different contexts.

/**
 * Scope interface for managing instance lifecycles within contexts
 * @param C Type of the context for this scope
 */
interface Scope<C> {
    /**
     * Get the registry for a specific context
     * @param context The context value for this scope
     * @return ScopeRegistry for managing instances in this context
     */
    fun getRegistry(context: C): ScopeRegistry
}

/**
 * Registry for storing and retrieving scoped instances
 */
interface ScopeRegistry {
    /**
     * Get or create an instance using the provided creator
     * @param key Unique key for the instance
     * @param sync Whether to synchronize access
     * @param creator Function to create the instance if not found
     * @return The stored or newly created instance
     */
    fun <T> get(key: Any, sync: Boolean, creator: () -> T): T
    
    /**
     * Clear all instances from this registry
     */
    fun clear()
}

/**
 * Multi-item scope registry using concurrent map for thread safety
 */
class StandardScopeRegistry : ScopeRegistry {
    // Implementation uses ConcurrentHashMap for thread-safe access
}

/**
 * Single-item scope registry optimized for performance
 */
class SingleItemScopeRegistry : ScopeRegistry {
    // Optimized for scopes that only hold one instance
}

/**
 * Global scope not bound to any specific context
 */
class UnboundedScope : Scope<Any> {
    // Provides global singleton behavior
}

/**
 * Simple scope implementation without context dependency
 */
class NoScope : Scope<Any> {
    // Basic scope without advanced features
}

/**
 * Hierarchical scope with parent-child relationships
 * @param C Child context type
 * @param PC Parent context type
 */
class SubScope<C : Any, PC : Any>(
    private val parentScope: Scope<PC>,
    private val getParentContext: (C) -> PC
) : Scope<C>

Scope Builders and Context Binding

DSL methods for creating scoped binding contexts and managing scope hierarchies.

/**
 * Base builder interface for scoped and context bindings
 * @param C The context type for this builder
 */
interface DI.BindBuilder<C : Any> {
    /** The context type for all bindings in this DSL context */
    val contextType: TypeToken<C>
    
    /** Whether the context is explicitly set */
    val explicitContext: Boolean
    
    /**
     * Builder with scope support for scoped singletons and multitons
     * @param C The scope's context type
     */
    interface WithScope<C : Any> : BindBuilder<C> {
        /** The scope for all bindings in this DSL context */
        val scope: Scope<C>
    }
}

/**
 * Create a scoped binding builder
 * @param scope The scope to use for bindings
 * @return BindBuilder with scope support
 */
fun <C : Any> DI.Builder.scoped(scope: Scope<C>): DI.BindBuilder.WithScope<C>

/**
 * Create a context-aware binding builder
 * @return BindBuilder for the specified context type
 */
inline fun <reified C : Any> DI.Builder.contexted(): DI.BindBuilder<C>

Context Translation

Advanced context translation system for converting between different context types and scoped dependency resolution.

/**
 * Interface for translating between different context types
 * @param C Source context type
 * @param S Target context type
 */
interface ContextTranslator<C, S> {
    /**
     * Translate a context for dependency resolution
     * @param key The binding key being resolved
     * @param context The source context
     * @return Translated context or null if translation not possible
     */
    fun translate(key: DI.Key<*, *, *>, context: C): S?
}

/**
 * Create a context translator using a transformation function
 * @param t Function that performs the context translation
 * @return ContextTranslator instance
 */
fun <C, S> contextTranslator(t: DirectDI.(C) -> S?): ContextTranslator<C, S>

/**
 * Create a context finder that locates context from DirectDI
 * @param t Function that finds the context
 * @return ContextTranslator that uses the finder function
 */
fun <S> contextFinder(t: DirectDI.() -> S): ContextTranslator<Any, S>

/**
 * Register a context translator with the DI builder
 * @param translator The context translator to register
 */
fun <C, S> DI.Builder.registerContextTranslator(translator: ContextTranslator<C, S>)

/**
 * Register a context finder with the DI builder
 * @param t Function that finds the context
 */
fun <S> DI.Builder.registerContextFinder(t: DirectDI.() -> S)

JVM-Specific Scopes

Platform-specific scoping implementations optimized for JVM environments with advanced memory management.

/**
 * Scope using weak references for automatic cleanup (JVM only)
 * @param C Context type for the weak scope
 */
class WeakContextScope<C : Any> : Scope<C> {
    // Uses WeakHashMap for automatic context cleanup
    // when contexts are garbage collected
}

/**
 * Reference makers for different memory management strategies (JVM only)
 */
// Thread-local references for thread-scoped singletons
val threadLocal: RefMaker

// Soft references for memory-sensitive caching
val softReference: RefMaker

// Weak references for automatic cleanup
val weakReference: RefMaker

Context Operations

Advanced context manipulation and scoped dependency access patterns for fine-grained dependency control.

/**
 * Create a new DIAware with different context
 * @param context New context for scoped dependencies
 * @param trigger Optional trigger for dependency resolution
 * @return New DIAware instance with updated context
 */
fun DIAware.On(context: DIContext<*> = this.diContext, trigger: DITrigger? = this.diTrigger): DI

/**
 * Create a new DIAware with typed context
 * @param context Context value
 * @param trigger Optional trigger for dependency resolution
 * @return New DIAware instance with typed context
 */
fun <C : Any> DIAware.on(context: C, trigger: DITrigger? = this.diTrigger): DI

/**
 * Create a new DirectDI with different context
 * @param context Context value for scoped dependencies
 * @return DirectDI instance with the specified context
 */
fun <C : Any> DirectDIAware.on(context: C): DirectDI

Usage Examples:

// Define context types
data class UserSession(val userId: String, val sessionId: String)
data class RequestContext(val requestId: String, val clientId: String)

// Create scoped DI with multiple contexts
val di = DI {
    // Global singleton (no scope)
    bind<ConfigService>() with singleton { ConfigServiceImpl() }
    
    // Request-scoped dependencies
    scoped(UnboundedScope).apply {
        bind<RequestLogger>() with singleton { RequestLoggerImpl() }
    }
    
    // User session scoped dependencies
    contexted<UserSession>().apply {
        bind<UserPreferences>() with singleton { 
            UserPreferencesImpl(diContext.value.userId) 
        }
        bind<UserCache>() with singleton { 
            UserCacheImpl(diContext.value.sessionId) 
        }
    }
    
    // JVM-specific: Thread-local scope for per-thread singletons
    bind<Database>() with singleton(ref = threadLocal) { 
        DatabaseConnection() 
    }
    
    // Context translator for automatic context conversion
    registerContextTranslator<RequestContext, UserSession> { requestCtx ->
        // Convert request context to user session context
        val userId = userService.getUserId(requestCtx.clientId)
        UserSession(userId, generateSessionId())
    }
}

// Using scoped dependencies
class RequestHandler : DIAware {
    override val di = di
    
    fun handleRequest(request: HttpRequest) {
        val requestContext = RequestContext(request.id, request.clientId)
        
        // Switch to request context
        val requestDI = on(requestContext)
        
        // These use request-scoped context
        val logger: RequestLogger by requestDI.instance()
        val userPrefs: UserPreferences by requestDI.instance() // Auto-translated from RequestContext
        val userCache: UserCache by requestDI.instance()
        
        logger.info("Processing request ${request.id}")
        val preferences = userPrefs.load()
        userCache.store("last_request", request.timestamp)
    }
}

// Custom scope implementation
class TimedScope(private val ttlMs: Long) : Scope<Any> {
    private val registries = mutableMapOf<Any, TimedScopeRegistry>()
    
    override fun getRegistry(context: Any): ScopeRegistry {
        return registries.getOrPut(context) { TimedScopeRegistry(ttlMs) }
    }
    
    private class TimedScopeRegistry(private val ttlMs: Long) : ScopeRegistry {
        private data class TimedEntry<T>(val value: T, val timestamp: Long)
        private val cache = mutableMapOf<Any, TimedEntry<*>>()
        
        override fun <T> get(key: Any, sync: Boolean, creator: () -> T): T {
            val now = System.currentTimeMillis()
            val entry = cache[key] as? TimedEntry<T>
            
            return if (entry != null && (now - entry.timestamp) < ttlMs) {
                entry.value
            } else {
                val newValue = creator()
                cache[key] = TimedEntry(newValue, now)
                newValue
            }
        }
        
        override fun clear() = cache.clear()
    }
}

// Using custom scopes
val timedDI = DI {
    val fiveMinuteScope = TimedScope(5 * 60 * 1000) // 5 minutes TTL
    
    scoped(fiveMinuteScope).apply {
        bind<ExpensiveService>() with singleton { 
            ExpensiveServiceImpl().also { 
                it.loadData() // Expensive initialization
            }
        }
    }
}

// Hierarchical scoping
class ApplicationService : DIAware {
    override val di = DI {
        // Application-level scope
        bind<AppConfig>() with singleton { AppConfigImpl() }
        
        // Module-level scope (child of application scope)
        val moduleScope = SubScope(UnboundedScope) { _: ModuleContext -> Unit }
        scoped(moduleScope).apply {
            bind<ModuleService>() with singleton { ModuleServiceImpl(instance()) }
        }
        
        // Request-level scope (child of module scope)
        contexted<RequestContext>().apply {
            bind<RequestProcessor>() with singleton { 
                RequestProcessorImpl(instance(), instance()) // Gets from parent scopes
            }
        }
    }
    
    private val appConfig: AppConfig by instance() // Application scope
    
    fun processRequest(requestContext: RequestContext) {
        val requestDI = on(requestContext)
        val processor: RequestProcessor by requestDI.instance() // Request scope
        processor.process()
    }
}

// JVM-specific weak reference scoping
val jvmDI = DI {
    // Weak context scope - automatically cleans up when contexts are GC'd
    val weakScope = WeakContextScope<UserSession>()
    scoped(weakScope).apply {
        bind<UserData>() with singleton { 
            UserDataImpl(diContext.value.userId) 
        }
    }
    
    // Different reference types for memory management
    bind<ImageCache>() with singleton(ref = softReference) { 
        ImageCacheImpl() // Uses soft references, cleared under memory pressure
    }
    
    bind<ThreadContext>() with singleton(ref = threadLocal) { 
        ThreadContextImpl() // One per thread
    }
    
    bind<TemporaryData>() with singleton(ref = weakReference) { 
        TemporaryDataImpl() // Automatically cleaned up
    }
}

// Context translation for complex scenarios
val translatingDI = DI {
    // Register multiple context translators
    registerContextTranslator<HttpRequest, UserSession> { request ->
        val session = sessionService.getSession(request.sessionToken)
        UserSession(session.userId, session.id)
    }
    
    registerContextTranslator<UserSession, UserPermissions> { session ->
        permissionService.getPermissions(session.userId)
    }
    
    registerContextFinder<DatabaseConnection> {
        connectionPool.getConnection()
    }
    
    // Bindings that use translated contexts
    contexted<UserSession>().apply {
        bind<UserService>() with singleton { UserServiceImpl(diContext.value.userId) }
    }
    
    contexted<UserPermissions>().apply {
        bind<AuthorizationService>() with singleton { 
            AuthorizationServiceImpl(diContext.value) 
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-kodein-di--kodein-di

docs

advanced-features.md

binding-dsl.md

container-configuration.md

direct-access.md

index.md

lazy-property-delegation.md

scoping-and-context.md

tile.json