Core dependency injection framework for Kotlin Multiplatform projects targeting WebAssembly JavaScript environments
—
Component interfaces and extension functions for seamless dependency injection integration in your classes.
Basic interface for classes that need access to dependency injection.
/**
* Interface for classes that need access to the Koin container
* Provides access to dependency injection functionality
*/
interface KoinComponent {
/**
* Get the Koin container instance
* @return Koin container for dependency resolution
*/
fun getKoin(): Koin
}Usage Examples:
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koin.core.component.get
class UserController : KoinComponent {
// Lazy injection using delegate
private val userService: UserService by inject()
private val emailService: EmailService by inject(named("primary"))
fun registerUser(userData: UserData): User {
val user = userService.createUser(userData)
// Direct resolution
val validator = get<UserValidator>()
validator.validate(user)
emailService.sendWelcomeEmail(user)
return user
}
fun handleSpecialCase() {
// Access Koin container directly
val koin = getKoin()
val specialService = koin.getOrNull<SpecialService>()
if (specialService != null) {
specialService.handleSpecialLogic()
}
}
}Extension functions that provide convenient dependency injection methods for KoinComponent implementations.
/**
* Get a dependency instance from the component's Koin container
* @param qualifier - Optional qualifier to distinguish multiple instances
* @param parameters - Optional parameters to pass to the factory function
* @return Instance of the requested type
*/
inline fun <reified T : Any> KoinComponent.get(qualifier: Qualifier? = null, noinline parameters: ParametersDefinition? = null): T
/**
* Get a lazy dependency from the component's Koin container
* @param qualifier - Optional qualifier to distinguish multiple instances
* @param mode - Thread safety mode for the lazy delegate
* @param parameters - Optional parameters to pass to the factory function
* @return Lazy delegate that resolves the dependency on first access
*/
inline fun <reified T : Any> KoinComponent.inject(qualifier: Qualifier? = null, mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(), noinline parameters: ParametersDefinition? = null): Lazy<T>Usage Examples:
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koin.core.component.get
import org.koin.core.parameter.parametersOf
import org.koin.core.qualifier.named
class OrderService : KoinComponent {
// Lazy injection - resolved on first access
private val paymentService: PaymentService by inject()
private val inventoryService: InventoryService by inject(named("warehouse"))
// Lazy injection with parameters
private val shippingCalculator: ShippingCalculator by inject {
parametersOf("standard")
}
// Note: For optional services, use getKoin().getOrNull() in methods
fun processOrder(order: Order): OrderResult {
// Direct resolution when needed
val validator = get<OrderValidator>()
validator.validateOrder(order)
// Use injected services
val paymentResult = paymentService.processPayment(order.payment)
if (!paymentResult.success) {
return OrderResult.PaymentFailed
}
// Check inventory
val available = inventoryService.checkAvailability(order.items)
if (!available) {
return OrderResult.OutOfStock
}
// Calculate shipping
val shippingCost = shippingCalculator.calculate(order.address, order.items)
// Apply promotions if available
val promotionService = getKoin().getOrNull<PromotionService>()
val finalTotal = promotionService?.applyPromotions(order.total) ?: order.total
return OrderResult.Success(finalTotal + shippingCost)
}
}Interface for components that manage their own scope lifecycle.
/**
* Interface for components that manage their own scope
* Extends KoinComponent with scope management capabilities
*/
interface KoinScopeComponent : KoinComponent {
/**
* The scope associated with this component
* Components are responsible for managing their scope lifecycle
*/
val scope: Scope
}Usage Examples:
import org.koin.core.component.KoinScopeComponent
import org.koin.core.scope.Scope
import org.koin.core.qualifier.named
class WebRequestHandler : KoinScopeComponent {
// Create scope for this request handler
override val scope: Scope by lazy {
getKoin().createScope<RequestScope>("request-${generateId()}")
}
// Inject from the component's scope
private val requestContext: RequestContext by scope.inject()
private val userSession: UserSession by scope.inject()
private val requestLogger: RequestLogger by scope.inject()
fun handleRequest(request: HttpRequest): HttpResponse {
requestLogger.logStart(request)
try {
val user = userSession.getCurrentUser()
val response = processRequest(request, user)
requestLogger.logSuccess(response)
return response
} catch (e: Exception) {
requestLogger.logError(e)
throw e
}
}
fun cleanup() {
// Close scope when component is done
scope.close()
}
}
// Usage pattern with try-finally
class RequestProcessor {
fun processRequest(requestData: RequestData) {
val handler = WebRequestHandler()
try {
val response = handler.handleRequest(requestData.toHttpRequest())
// Handle response...
} finally {
handler.cleanup() // Ensure scope is closed
}
}
}Extension functions that provide scope management utilities for KoinScopeComponent implementations.
/**
* Get the scope ID for any object based on its class name
* @param T - Type of the object
* @return Generated scope ID string
*/
fun <T : Any> T.getScopeId(): String
/**
* Get the scope name (type qualifier) for any object
* @param T - Type of the object
* @return TypeQualifier based on the object's class
*/
fun <T : Any> T.getScopeName(): TypeQualifier
/**
* Create a scope for a KoinScopeComponent
* @param scopeId - Scope identifier (default: generated from object)
* @param source - Optional source object
* @param scopeArchetype - Optional scope archetype
* @return New Scope instance
*/
fun <T : KoinScopeComponent> T.createScope(scopeId: ScopeID = getScopeId(), source: Any? = null, scopeArchetype: TypeQualifier? = null): Scope
/**
* Create a scope for a KoinScopeComponent with source
* @param source - Source object for the scope
* @return New Scope instance
*/
fun <T : KoinScopeComponent> T.createScope(source: Any? = null): Scope
/**
* Get the scope for a KoinScopeComponent or null if not created
* @return Existing scope or null
*/
fun <T : KoinScopeComponent> T.getScopeOrNull(): Scope?
/**
* Create a new scope lazily for a KoinScopeComponent
* @return Lazy delegate that creates the scope on first access
*/
fun <T : KoinScopeComponent> T.newScope(): Lazy<Scope>
/**
* Get existing scope or create a new one lazily for a KoinScopeComponent
* @return Lazy delegate that gets or creates the scope on first access
*/
fun <T : KoinScopeComponent> T.getOrCreateScope(): Lazy<Scope>Usage Examples:
import org.koin.core.component.KoinScopeComponent
import org.koin.core.component.*
// Different scope creation patterns
class UserSessionManager : KoinScopeComponent {
// Option 1: Create scope immediately
override val scope: Scope = createScope(source = this)
private val sessionStore: SessionStore by scope.inject()
private val userCache: UserCache by scope.inject()
}
class LazySessionManager : KoinScopeComponent {
// Option 2: Create scope lazily
override val scope: Scope by newScope()
private val sessionStore: SessionStore by scope.inject()
}
class AdaptiveSessionManager : KoinScopeComponent {
// Option 3: Get or create scope lazily
override val scope: Scope by getOrCreateScope()
private val sessionStore: SessionStore by scope.inject()
fun initializeSession(userId: String) {
// Scope is created here on first access
scope.declare(UserId(userId))
// Generate scope ID for debugging
val scopeId = getScopeId()
val scopeName = getScopeName()
println("Session scope: $scopeName ($scopeId)")
}
}
// Usage with explicit scope management
class RequestScopedService : KoinScopeComponent {
override val scope: Scope by lazy {
val myScope = createScope("custom-${System.currentTimeMillis()}")
// Register cleanup callback
myScope.registerCallback(object : ScopeCallback {
override fun onScopeCreated(qualifier: Qualifier, scope: Scope) {
println("Scope created: ${scope.id}")
}
override fun onScopeClosed(qualifier: Qualifier, scope: Scope) {
println("Scope closed: ${scope.id}")
}
})
myScope
}
private val requestProcessor: RequestProcessor by scope.inject()
fun processAndCleanup() {
try {
requestProcessor.process()
} finally {
// Check if scope exists before closing
getScopeOrNull()?.close()
}
}
}Common patterns for using components effectively.
Singleton Component Pattern:
// Global singleton component
object ApplicationServices : KoinComponent {
val configService: ConfigService by inject()
val logService: LogService by inject()
fun initialize() {
configService.loadConfiguration()
logService.initialize()
}
}
// Usage
ApplicationServices.initialize()
val config = ApplicationServices.configService.getConfig("app.name")Factory Component Pattern:
// Component factory for creating instances
class ComponentFactory : KoinComponent {
fun createUserProcessor(userId: String): UserProcessor {
return get<UserProcessor> { parametersOf(userId) }
}
fun createApiClient(endpoint: String): ApiClient {
return get<ApiClient> { parametersOf(endpoint) }
}
}
// Usage
val factory = ComponentFactory()
val processor = factory.createUserProcessor("user-123")
val client = factory.createApiClient("https://api.example.com")Scoped Component Lifecycle Pattern:
// Base class for scoped components
abstract class ScopedComponent : KoinScopeComponent {
abstract val scopeQualifier: Qualifier
override val scope: Scope by lazy {
getKoin().createScope("${scopeQualifier.value}-${generateId()}", scopeQualifier)
}
open fun initialize() {
// Subclasses can override for initialization
}
open fun cleanup() {
if (!scope.closed) {
scope.close()
}
}
}
// Request-scoped component
class RequestComponent : ScopedComponent() {
override val scopeQualifier = named("request")
private val requestHandler: RequestHandler by scope.inject()
private val requestValidator: RequestValidator by scope.inject()
override fun initialize() {
// Request-specific initialization
scope.declare(RequestId(generateRequestId()))
}
}
// Usage with automatic cleanup
inline fun <T> withRequestComponent(block: (RequestComponent) -> T): T {
val component = RequestComponent()
return try {
component.initialize()
block(component)
} finally {
component.cleanup()
}
}
// Usage
withRequestComponent { component ->
val result = component.requestHandler.handle(request)
// Component is automatically cleaned up
result
}/**
* Base interface for dependency injection components
*/
interface KoinComponent {
fun getKoin(): Koin
}
/**
* Interface for components with scope management
*/
interface KoinScopeComponent : KoinComponent {
val scope: Scope
}
/**
* Generate a unique ID for scopes and components
* @return Generated unique identifier string
*/
fun generateId(): String
/**
* Type alias for type-based qualifiers
*/
class TypeQualifier : Qualifier {
val type: KClass<*>
}Install with Tessl CLI
npx tessl i tessl/maven-io-insert-koin--koin-core-wasm-js