kotlinintermediate
Dependency Injection with Koin
Set up Koin DI: modules, scoped instances, factory vs single, and parameterized injection.
kotlinPress ⌘/Ctrl + Shift + C to copy
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.core.module.dsl.*
import org.koin.core.parameter.parametersOf
import org.koin.dsl.*
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koin.core.qualifier.named
// Interfaces
interface Logger {
fun log(message: String)
}
interface UserRepository {
fun findById(id: String): Map<String, Any>?
fun save(user: Map<String, Any>)
}
interface NotificationService {
fun send(userId: String, message: String)
}
// Implementations
class ConsoleLogger : Logger {
override fun log(message: String) = println("[LOG] $message")
}
class InMemoryUserRepository(private val logger: Logger) : UserRepository {
private val users = mutableMapOf<String, Map<String, Any>>()
override fun findById(id: String): Map<String, Any>? {
logger.log("Finding user: $id")
return users[id]
}
override fun save(user: Map<String, Any>) {
val id = user["id"] as String
logger.log("Saving user: $id")
users[id] = user
}
}
class EmailNotificationService(
private val logger: Logger,
private val smtpHost: String
) : NotificationService {
override fun send(userId: String, message: String) {
logger.log("Sending email via $smtpHost to $userId: $message")
}
}
// Service with dependencies
class UserService(
private val repo: UserRepository,
private val notification: NotificationService,
private val logger: Logger
) {
fun createUser(id: String, name: String) {
logger.log("Creating user $id")
repo.save(mapOf("id" to id, "name" to name))
notification.send(id, "Welcome, $name!")
}
fun getUser(id: String) = repo.findById(id)
}
// Parameterized factory
class ApiClient(val baseUrl: String, private val logger: Logger) {
fun get(path: String): String {
logger.log("GET $baseUrl$path")
return "{\"status\": \"ok\"}"
}
}
// Koin modules
val coreModule = module {
single<Logger> { ConsoleLogger() }
}
val dataModule = module {
single<UserRepository> { InMemoryUserRepository(get()) }
}
val serviceModule = module {
single<NotificationService> {
EmailNotificationService(get(), "smtp.example.com")
}
single { UserService(get(), get(), get()) }
// Factory — new instance each time
factory { (baseUrl: String) ->
ApiClient(baseUrl, get())
}
// Named instances
single(named("primary")) { mapOf("url" to "https://primary.api.com") }
single(named("fallback")) { mapOf("url" to "https://fallback.api.com") }
}
// KoinComponent — for classes outside DI graph
class Application : KoinComponent {
private val userService: UserService by inject()
private val logger: Logger by inject()
fun run() {
logger.log("Application starting")
userService.createUser("1", "Alice")
userService.createUser("2", "Bob")
val user = userService.getUser("1")
logger.log("Found: $user")
}
}
fun main() {
// Start Koin
startKoin {
modules(coreModule, dataModule, serviceModule)
}
// Run app
val app = Application()
app.run()
// Parameterized factory
val koin = org.koin.core.context.GlobalContext.get()
val client = koin.get<ApiClient> { parametersOf("https://api.example.com") }
println(client.get("/users"))
// Named instances
val primary = koin.get<Map<String, String>>(named("primary"))
println("Primary: $primary")
stopKoin()
}Use Cases
- Application dependency management
- Testable service architecture
- Modular application composition
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
typescriptadvanced
Dependency Injection Container
Build a lightweight DI container with singleton and transient scopes for testable Node.js applications.
Best for: Testable application architecture
#nodejs#dependency-injection
scalaintermediate
Dependency Injection Patterns
Implement dependency injection in Scala: cake pattern, constructor injection, and ZIO ZLayer.
Best for: Decoupled service architecture
#scala#di
kotlinbeginner
Null Safety — Elvis, Safe Call, and let
Master Kotlin null safety: safe calls, Elvis operator, let/also scoping, and smart casts.
Best for: Safe navigation through nullable chains
#kotlin#null-safety
kotlinbeginner
Data Classes — Copy, Destructure, and Equals
Use data classes for immutable models: auto-generated equals, hashCode, copy, and destructuring.
Best for: Immutable domain models and DTOs
#kotlin#data-class