Koin — qué es, principios de Dependency Injection y cómo funciona

Autor: IT Sectr Publicado: 2026-05-04 Tiempo de lectura: 8 min

Koin es un framework de DI para Kotlin que funciona sin generación de código, reflexión ni anotaciones. La biblioteca utiliza DSL para describir módulos e inyecta dependencias a través de un contenedor ligero con soporte para Android, Ktor y Multiplatform. Según la documentación oficial de Koin, el framework proporciona módulos, alcances y soporte integrado para Jetpack Compose con un mínimo de boilerplate.

Puntos clave

  • Koin es un framework de DI para Kotlin que utiliza DSL sin reflexión ni generación de código.
  • Module es una agrupación lógica de registros de dependencias mediante las funciones single y factory.
  • single registra un singleton cuya instancia se crea una sola vez.
  • factory crea una nueva instancia en cada solicitud.
  • Scope es un mecanismo que vincula el ciclo de vida de la dependencia a un componente, como una Activity.

Qué es Koin y en qué se diferencia

Koin es un framework de DI para Kotlin escrito en lenguaje puro sin usar reflexión, anotaciones ni generación de código. A diferencia de Dagger Hilt, que requiere un procesador de anotaciones y generación de código en tiempo de compilación, Koin funciona completamente en tiempo de ejecución utilizando un DSL ligero para describir módulos.

La idea principal de Koin es proporcionar una API simple para registrar y resolver dependencias sin necesidad de aprender conceptos complejos de grafos de dependencias y árboles de componentes. El desarrollador describe qué clases están disponibles para el contenedor, y Koin las inyecta automáticamente a través del constructor o delegados perezosos by inject. El framework es totalmente compatible con Kotlin Multiplatform, lo que permite un enfoque DI único en Android, iOS y el lado del servidor.

Según la encuesta de la Comunidad de Desarrolladores Kotlin (2025), Koin se utiliza en el 31% de los proyectos Android comerciales, solo superado por Hilt (47%). La razón principal para elegirlo es la facilidad de configuración y la ausencia de necesidad de generación de código, lo que acelera la compilación del proyecto.

Elija Koin para proyectos medianos y grandes donde sea importante un inicio rápido del desarrollo, o para soluciones Kotlin Multiplatform donde Hilt no esté disponible por razones arquitectónicas.

Koin no utiliza reflexión ni generación de código — todos los registros se basan en funciones inline con tipos reified que sustituyen el tipo concreto en el cuerpo de la función en tiempo de compilación. Esto convierte a Koin en uno de los frameworks DI más ligeros en términos de tamaño final del APK: agregar Koin aumenta el tamaño de la aplicación solo entre 100 y 150 KB, mientras que Dagger Hilt añade unos 500 KB debido al código generado.

Cómo funcionan el contenedor y el DSL de Koin

El contenedor Koin se inicializa mediante la función startKoin, que acepta una lambda con configuración. Dentro de esta lambda se describen los módulos con registros, el bloque de construcción principal de la lógica DI.

startKoin y los módulos

La función startKoin crea un contenedor global accesible desde cualquier parte de la aplicación a través de GlobalContext; sin embargo, en proyectos multimódulo se recomienda usar KoinApplication para crear contenedores aislados. En Android, se utiliza AndroidContext para la inicialización, que se vincula automáticamente al ciclo de vida de Application. Los módulos se registran mediante el parámetro modules, que acepta una lista de instancias de Module.

kotlin
val networkModule = module {
    single {
        OkHttpClient()
    }
    single {
        Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .build()
    }
}

startKoin {
    modules(networkModule)
}

Cada módulo contiene definiciones mediante single (singleton) o factory (nueva instancia). Las definiciones pueden hacer referencia a otras dependencias registradas a través de get(), formando un grafo de inyección sin especificación explícita de tipos y sin código boilerplate.

DSL y funciones inline

Koin utiliza activamente funciones inline con parámetros reified para inferir el tipo del contexto. Esto permite escribir registros sin especificar explícitamente la clase: single { MyService() } determina automáticamente el tipo por el valor de retorno de la lambda.

A diferencia de Dagger, Koin no verifica el grafo de dependencias en tiempo de compilación — todos los errores se detectan en tiempo de ejecución al primer acceso a una dependencia no resuelta. Esto es un compromiso que simplifica significativamente el código y acelera las compilaciones, pero requiere cobertura de pruebas de la configuración DI. Muchos equipos eligen Koin precisamente por la velocidad de desarrollo y la simplicidad, a pesar de la falta de verificaciones en tiempo de compilación.

En la versión de Koin 3.5, apareció la verificación experimental del grafo en tiempo de compilación a través del plugin Koin Annotations. El desarrollador agrega las anotaciones @Module y @KoinComponent, y el plugin genera código de validación que se ejecuta durante la compilación. Sin embargo, la principal ventaja de Koin — la ausencia de generación de código — se pierde en este modo, por lo que la mayoría de los equipos continúan utilizando el enfoque DSL clásico con verificaciones en tiempo de ejecución mediante pruebas.

Métodos de inyección de dependencias en Koin

Koin proporciona varias formas de inyectar dependencias: by inject(), get() y paso directo por constructor. La elección depende del contexto de uso.

by inject() — inyección perezosa

El delegado by inject es el método de inyección más común en ViewModels y fragmentos de Android. La dependencia se inicializa de forma perezosa, solo en el primer acceso a la propiedad. Esto es eficiente para servicios que consumen muchos recursos y que pueden no ser necesarios de inmediato.

kotlin
class MainViewModel : ViewModel() {
    private val repository: UserRepository by inject()

    fun loadUsers() {
        repository.fetchAll()
    }
}

get() — obtención explícita

La función get devuelve una instancia de dependencia de inmediato. Se utiliza dentro de lambdas de fábrica durante el registro o cuando se necesita la dependencia en un contexto síncrono sin inicialización perezosa. A diferencia de by inject(), get() no admite carga perezosa y requiere que el contenedor ya esté inicializado en el momento de la llamada.

Módulos y alcances en Koin

Scope en Koin es un mecanismo para vincular el ciclo de vida de las dependencias a un componente específico, como una Activity, Fragment o sesión personalizada. Esta es una funcionalidad clave para la gestión de memoria en aplicaciones Android.

scope — vinculación a un componente

La función scope dentro de un módulo crea un alcance que vive mientras vive el componente vinculado. Todas las dependencias registradas en el alcance se destruyen cuando se cierra, lo que evita fugas de memoria.

kotlin
val userScope = module {
    scope<UserSession> {
        scoped {
            UserRepository(get())
        }
        scoped {
            SessionManager(get())
        }
    }
}

La función scoped registra una dependencia que solo existirá dentro del alcance. Cuando se cierra el alcance, todos los objetos scoped quedan disponibles para el recolector de basura.

single vs factory

single registra una única instancia para toda la aplicación con inicialización perezosa. Se utiliza para servicios sin estado: clientes de red, cachés, registradores.

factory crea una nueva instancia en cada llamada a get(). Se aplica a ViewModel, repositorios y objetos con estado donde se necesita una instancia nueva en cada acceso.

Koin en proyectos Android

La integración de Koin en un proyecto Android es mínima: basta con agregar una dependencia en build.gradle y llamar a startKoin en Application.onCreate. Koin proporciona módulos para integrarse con Jetpack Compose, Navigation y WorkManager, lo que lo convierte en una alternativa completa a Hilt.

La biblioteca especial koin-android-compose permite inyectar dependencias directamente en funciones Composable a través de koinViewModel() y koinInject(). Esto elimina la necesidad de pasar el contenedor por los parámetros de cada pantalla y hace que el código de ViewModel sea más limpio mediante la vinculación automática al ciclo de vida.

Según Google I/O 2024, Jetpack Compose se convirtió en el framework principal para nuevos proyectos Android. Koin proporciona soporte nativo para Compose sin configuración adicional, vinculando automáticamente los alcances al ciclo de vida de ViewModel a través de koinViewModel() teniendo en cuenta el contexto de corrutinas.

Para pruebas, Koin proporciona las funciones koinTest y koinTestRule, que crean un contenedor de prueba aislado con módulos de prueba y lo cierran automáticamente después de completar la prueba. Esto garantiza el aislamiento de las pruebas y evita fugas de estado entre casos de prueba.

La integración de Koin con Jetpack Navigation se implementa a través del módulo koin-androidx-navigation. El ViewModel de cada pantalla recibe automáticamente las dependencias a través de by viewModel() con paso de SavedStateHandle para preservar el estado al rotar la pantalla y recuperarlo después de suspender la aplicación.

Para pruebas unitarias de ViewModel con Koin, se utiliza koinTestRule de la biblioteca koin-test-junit5 o koin-test-junit4. La regla crea un contenedor aislado con módulos de prueba antes de cada prueba y lo cierra automáticamente al finalizar, evitando fugas de estado entre casos de prueba. Las dependencias reales se reemplazan con mocks mediante MockK: un módulo con registros single { mockk() } sobrescribe el módulo principal, y el ViewModel en la prueba obtiene un comportamiento predecible de las dependencias.

Una de las características clave de Koin 3.x es el soporte para Ktor para crear aplicaciones de servidor en Kotlin y Compose Multiplatform para aplicaciones de escritorio. Esto convierte a Koin en el único framework DI que cubre las tres plataformas de Kotlin sin cambiar el paradigma de inyección. El módulo koin-ktor permite registrar dependencias mediante install(Koin) en el bloque Application e inyectar servicios en las rutas a través de by inject() igual que en Android. Esto hace de Koin una solución DI universal para proyectos Kotlin de cualquier arquitectura, desde un cliente móvil hasta un backend de servidor.

La integración de Koin con Jetpack Navigation a través del módulo koin-androidx-navigation elimina la necesidad de crear manualmente un ViewModelProvider.Factory para cada pantalla. Para proyectos multimódulo, Koin admite la carga perezosa de módulos mediante loadKoinModules, lo que permite que cada módulo de funcionalidad conecte su configuración DI de forma independiente.

Preguntas frecuentes

¿En qué se diferencia Koin de Dagger Hilt?

Koin funciona en tiempo de ejecución sin generación de código ni anotaciones, lo que acelera las compilaciones pero no verifica el grafo de dependencias en tiempo de compilación. Hilt genera código en tiempo de compilación y detecta errores DI antes, pero requiere una configuración compleja y ralentiza la compilación.

¿Koin es compatible con Kotlin Multiplatform?

Sí, Koin es totalmente compatible con Kotlin Multiplatform. La biblioteca koin-core funciona en todas las plataformas Kotlin, mientras que koin-android y koin-compose añaden capacidades específicas de plataforma para Android e iOS respectivamente.

¿Cómo manejar dependencias circulares en Koin?

Las dependencias circulares provocan StackOverflowError en tiempo de ejecución. Koin no las detecta automáticamente. La solución es refactorizar la arquitectura: extraer una interfaz común, usar el patrón Listener/Observer o romper el ciclo mediante una fábrica con inicialización diferida.

¿Es necesario cerrar los alcances manualmente?

En Android, los alcances se pueden vincular al ciclo de vida de Activity o Fragment mediante AndroidScope. Cuando el componente se destruye, Koin cierra automáticamente el alcance correspondiente. En alcances personalizados (sesión de usuario), el cierre se realiza manualmente llamando a scope.close.

¿Cómo probar el código con Koin?

Utilice la función koinTest del módulo koin-test. Crea un contenedor aislado con módulos de prueba que se cierra automáticamente después de la prueba. Las dependencias reales se reemplazan con mocks mediante un módulo con Mockito o MockK.

Resumen

  • Koin es un framework DI para Kotlin con DSL sin reflexión, anotaciones ni generación de código.
  • Los módulos agrupan registros mediante single (singleton) y factory (nueva instancia).
  • startKoin inicializa un contenedor global accesible mediante by inject() y get().
  • Scope vincula el ciclo de vida de las dependencias al ciclo de vida del componente Android.
  • La inyección en Compose se realiza mediante koinViewModel() y koinInject().
  • Koin es compatible con Kotlin Multiplatform, lo que permite un enfoque DI único en todas las plataformas.
  • Los errores DI se detectan en tiempo de ejecución, por lo que la cobertura de pruebas de la configuración es obligatoria.

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también