Koin — Contenedor de Inyección de Dependencias para Kotlin
En el desarrollo moderno con Kotlin, la gestión de dependencias es una tarea importante. Koin es un contenedor ligero de Inyección de Dependencias (DI) escrito en Kotlin, que no requiere generación de código, reflexión ni configuración compleja. A diferencia de Dagger o Spring, Koin utiliza un DSL simple y se integra completamente con corrutinas y Flow. En esta página, analizaremos cómo instalar Koin, declarar módulos, inyectar dependencias y usarlo en proyectos educativos.
¿Qué es la Inyección de Dependencias?
La Inyección de Dependencias (DI) es un patrón en el que un objeto recibe sus dependencias desde el exterior, en lugar de crearlas él mismo. Esto simplifica las pruebas, hace que el código sea más flexible y reduce el acoplamiento. Koin implementa DI a través de un contenedor que gestiona la creación y el ciclo de vida de los objetos.
Instalación de Koin
Añada la siguiente dependencia al archivo build.gradle.kts:
// build.gradle.ktsdependencies { implementation("io.insert-koin:koin-core:3.5.3") // Para Android implementation("io.insert-koin:koin-android:3.5.3")}Conceptos principales de Koin
- Módulo (module) — bloque donde se declaran las dependencias.
- Definición (definition) — forma de crear un objeto (single, factory, scoped).
- Contenedor (KoinApplication) — inicia y gestiona los módulos.
- Inyección (inject) — obtención de dependencias mediante delegados o funciones.
Creación de módulos y declaración de dependencias
Koin utiliza DSL para describir módulos. Veamos un ejemplo con un repositorio y un servicio:
// Definimos la interfaz y la implementacióninterface UserRepository { fun getUser(id: Int): String}
class UserRepositoryImpl : UserRepository { override fun getUser(id: Int) = "User #$id"}
class UserService(private val repository: UserRepository) { fun printUser(id: Int) { println(repository.getUser(id)) }}
// Módulo de Koinval appModule = module { // single — crea una única instancia (singleton) single<UserRepository> { UserRepositoryImpl() } // factory — crea una nueva instancia en cada solicitud factory { UserService(get()) }}Inicio del contenedor
Antes de usar las dependencias, es necesario iniciar Koin:
import org.koin.core.context.startKoin
fun main() { startKoin { modules(appModule) } // Ahora podemos obtener dependencias val service: UserService by inject() service.printUser(42)}Formas de inyectar dependencias
- by inject() — obtención perezosa (recomendada).
- get() — obtención inmediata.
- Constructor — Koin pasa automáticamente las dependencias al constructor.
Ejemplo usando by inject():
class MyActivity : AppCompatActivity() { // Inyección perezosa private val repository: UserRepository by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Usamos repository }}Dependencias con ámbito (Scoped)
A veces es necesario crear una dependencia que viva dentro de un contexto específico (por ejemplo, una sesión de usuario). Para ello se usa scope:
val sessionModule = module { scope<UserSession> { scoped { UserSessionManager(get()) } }}Trabajo con corrutinas
Koin soporta corrutinas a través del módulo koin-core-coroutines. Ejemplo de obtención asíncrona:
import org.koin.core.component.KoinComponentimport org.koin.core.component.inject