DIP: fundamentos, inversión de dependencias en el desarrollo

Autor: IT Sectr Publicado: 2026-05-12 Tiempo de lectura: 9 min

DIP (Dependency Inversion Principle) — el quinto principio SOLID que define las reglas para construir dependencias entre módulos: los módulos de alto nivel no deben depender de módulos de bajo nivel, ambos deben depender de abstracciones. Las abstracciones no deben depender de los detalles — los detalles deben depender de las abstracciones. Este principio, descrito por Robert Martin en Clean Architecture (2017), está en la base de la arquitectura débilmente acoplada. Según este libro, el principio de inversión de dependencias elimina los acoplamientos rígidos entre las capas de la aplicación.

Puntos clave

  • DIP — principio de inversión de dependencias, el quinto de SOLID, sobre límites arquitectónicos
  • Los módulos de alto nivel no deben importar módulos de bajo nivel — solo abstracciones
  • DIP ≠ DI: Dependency Inversion es un principio arquitectónico, Dependency Injection es una forma de implementarlo
  • Las abstracciones pertenecen al módulo de alto nivel, las implementaciones al de bajo nivel
  • DIP invierte la jerarquía tradicional de dependencias en arquitecturas multicapa

¿Qué es DIP (Dependency Inversion Principle)?

DIP (Dependency Inversion Principle) es el principio de inversión de dependencias que invierte la visión tradicional sobre la dirección de las dependencias entre módulos. Los módulos de alto nivel (lógica de negocio) no deben depender directamente de módulos de bajo nivel (base de datos, red, UI). En su lugar, ambos niveles dependen de abstracciones definidas en el módulo de alto nivel.

La formulación formal de DIP incluye dos reglas: A — los módulos de alto nivel no deben depender de módulos de bajo nivel, ambos deben depender de abstracciones. B — las abstracciones no deben depender de los detalles, los detalles deben depender de las abstracciones. La segunda regla se deriva de la primera: si una abstracción depende de los detalles, no puede ser una base estable para un módulo de alto nivel.

Sin DIP, una arquitectura típica se ve así: BusinessLogic → DatabaseRepository — la lógica de negocio depende directamente de un repositorio concreto. Con DIP: BusinessLogic → DatabaseServiceInterface ← DatabaseRepository. BusinessLogic no sabe de la existencia de DatabaseRepository, solo conoce la interfaz DatabaseService, que se implementa fuera de la lógica de negocio.

Dirección de las dependencias en DIP

Inversión significa que el flujo de control y el flujo de dependencias van en direcciones opuestas. El flujo de control va de arriba abajo: UI → ViewModel → UseCase → Repository. El flujo de dependencias va de abajo arriba: Repository implementa una interfaz definida en UseCase. Repository (bajo nivel) depende de UseCase (alto nivel).

Esta inversión es la diferencia clave entre DIP y la separación habitual en capas. En la arquitectura tradicional por capas, cada capa depende de la capa inferior. En la arquitectura con DIP, todas las capas dependen de abstracciones, mientras que la implementación de estas abstracciones reside en la capa de infraestructura, que se “enchufa” a las capas superiores mediante mecanismos de DI.

Cómo funciona el principio de inversión de dependencias

El mecanismo de DIP se implementa definiendo abstracciones en los módulos de alto nivel y realizándolas en los módulos de bajo nivel. El módulo de alto nivel declara una interfaz para la funcionalidad que necesita. El módulo de bajo nivel implementa esa interfaz. El ensamblaje (wiring) ocurre en la raíz de composición de la aplicación.

El proceso de introducir DIP en código existente: extraer una interfaz para el módulo de bajo nivel, mover esta interfaz al módulo de alto nivel (o a una capa separada de abstracciones), reescribir la dependencia del módulo de alto nivel para que use la interfaz, hacer que el módulo de bajo nivel implemente esta interfaz. Después de estos pasos, la dirección de la dependencia se ha invertido.

DIP requiere un mecanismo de raíz de composición — un punto en la aplicación donde se crean y conectan todas las dependencias. En Android es Application.get() o el componente Hilt, en iOS — AppDelegate o SceneDelegate. La raíz de composición es el único lugar donde el código conoce las implementaciones concretas.

Aislamiento de capas mediante DIP

DIP crea límites arquitectónicos entre las capas de la aplicación. Cuando ViewModel depende de la interfaz UserRepository, se forma un límite entre las capas de presentación y dominio: ViewModel (presentación) no sabe de dónde vienen los datos. Este límite permite cambiar la implementación de UserRepository (Room → REST → Mock) sin afectar a ViewModel. Cuantos más límites de este tipo, más resistente es la aplicación a los cambios de frameworks y bibliotecas.

En la arquitectura Android recomendada por Google, DIP se implementa mediante UseCases que residen en la capa de dominio y dependen de interfaces Repository. RepositoryImpl están en la capa de datos e implementan estas interfaces. La capa de presentación (ViewModel) depende de UseCases. La dirección de las dependencias va de presentación a dominio, de dominio a datos — pero ninguna capa conoce las implementaciones concretas de otra capa.

Diferencia entre DIP y DI (Dependency Injection)

DIP y DI a menudo se confunden, pero son conceptos diferentes. DIP es un principio arquitectónico (QUÉ hacer: depender de abstracciones). DI es un patrón de implementación (CÓMO hacerlo: pasar dependencias mediante el constructor). DIP responde a la pregunta “¿en qué deben basarse los módulos?”, DI responde a “¿cómo obtienen los objetos sus dependencias?”.

Dependency Injection es una forma de inyectar dependencias en un objeto a través del constructor, método o propiedad. Cuando una clase Kotlin recibe una interfaz Repository mediante su constructor — eso es DI. El hecho de que una clase ViewModel dependa de la interfaz Repository y no de una implementación concreta de RoomRepository — eso es DIP. DI es la herramienta, DIP es el objetivo.

Se puede seguir DIP sin un framework DI: el ensamblaje manual de dependencias en la raíz de composición también es DI (manual DI). Se puede usar un framework DI (Dagger, Hilt, Koin) violando DIP: si ViewModel crea directamente un objeto Repository mediante new() — DIP se viola, incluso si el framework está instalado. DIP es una decisión arquitectónica, DI es un detalle técnico.

Ejemplos de DIP en el desarrollo móvil

Veamos un ejemplo en Android de aplicación de DIP a la capa de datos. Sin DIP, una ViewModel crea directamente una RoomDatabase y DAO. Con DIP — la ViewModel depende de la interfaz UserRepository, y la implementación concreta RoomUserRepository se proporciona externamente.

kotlin
// La abstracción pertenece a la capa de dominio (alto nivel)
interface UserRepository {
    fun getUser(id: Int): User
}

// La capa de dominio depende solo de la abstracción
class GetUserUseCase(
    private val repo: UserRepository
) {
    fun execute(id: Int): User = repo.getUser(id)
}

// La implementación en la capa de datos depende de la abstracción de la capa de dominio
class RoomUserRepository(
    private val dao: UserDao
) : UserRepository {
    override fun getUser(id: Int): User {
        return dao.getById(id)
    }
}

// Raíz de composición
class AppModule {
    fun provideUserRepository(dao: UserDao): UserRepository {
        return RoomUserRepository(dao)
    }
}

Un ejemplo en iOS con un Application Coordinator y un protocolo de navegación:

swift
// Abstracción de navegación en la capa de dominio
protocol AuthNavigation {
    func navigateToHome()
    func navigateToLogin()
}

// ViewModel depende de la abstracción, no de UIKit
final class AuthViewModel {
    private let navigation: AuthNavigation

    init(navigation: AuthNavigation) {
        self.navigation = navigation
    }

    func onLoginSuccess() {
        navigation.navigateToHome()
    }
}

// Coordinator (capa UIKit) implementa el protocolo de la capa de dominio
final class AppCoordinator: AuthNavigation {
    func navigateToHome() {
        // Código de navegación UIKit
    }
    func navigateToLogin() {
        // Código de navegación UIKit
    }
}

El punto clave: AuthViewModel (dominio) no sabe de la existencia de AppCoordinator (UIKit). Solo conoce el protocolo AuthNavigation. Si UIKit se reemplaza por SwiftUI mañana — AuthViewModel no requiere cambios. DIP hace que la capa de dominio sea independiente de los frameworks y bibliotecas de UI.

Herramientas para DIP: Dagger, Hilt, Koin

Hilt es la herramienta DI estándar para Android, recomendada por Google. Está integrada en Jetpack, soporta ViewModel, Fragment, Service y otros componentes de Android. Hilt automatiza la creación de la raíz de composición mediante anotaciones @Module, @Provides, @Inject. Usar Hilt no garantiza el cumplimiento de DIP — la interfaz UserRepository debe definirse en la capa de dominio, no en la capa de datos.

Koin es un framework DI ligero para Kotlin sin generación de código ni procesamiento de anotaciones. El DSL de Koin (module, single, factory) es más fácil de aprender, pero la verificación de dependencias ocurre en tiempo de ejecución, no en tiempo de compilación. Koin es popular en proyectos multiplataforma (KMP) gracias al soporte de iOS.

Dagger 2 es el predecesor de Hilt, todavía usado en proyectos grandes. Dagger genera código DI en tiempo de compilación, lo que proporciona el máximo rendimiento y diagnóstico de errores en tiempo de compilación. Hilt está construido sobre Dagger y proporciona una API simplificada. Para proyectos nuevos, Google recomienda Hilt como framework DI principal.

Organización de módulos DI por capas

Los módulos DI deben corresponder a las capas arquitectónicas y separarse en DomainModule, DataModule, PresentationModule. DomainModule proporciona solo abstracciones y UseCases. DataModule proporciona implementaciones para las abstracciones. PresentationModule conecta ViewModels con UseCases. Esta organización garantiza que la capa de dominio permanezca independiente de las bibliotecas de infraestructura.

Al migrar entre frameworks DI (por ejemplo, de Koin a Hilt), la estructura de DomainModule no cambia — solo cambian los métodos de conexión en DataModule y PresentationModule. DIP asegura el aislamiento de la lógica de dominio, mientras que el framework DI es un mecanismo técnico de conexión.

Preguntas frecuentes

¿Siempre hay que aplicar DIP?

DIP es necesario en los límites arquitectónicos — entre las capas de la aplicación (dominio → datos, presentación → dominio). Dentro de una misma capa, DIP puede ser excesivo. Por ejemplo, una clase utilitaria StringFormatter dentro de la capa de dominio no requiere una interfaz — si no hay razón para reemplazarla.

¿Es DIP lo mismo que inyección de dependencias?

No. DIP es un principio: los módulos deben depender de abstracciones. DI es un patrón: un objeto recibe dependencias desde el exterior en lugar de crearlas él mismo. DI es una forma de implementar DIP, pero se puede seguir DIP sin DI (mediante fábricas o service locator). DI sin DIP es posible pero no tiene valor arquitectónico.

¿Dónde deben definirse las interfaces para DIP?

Las interfaces pertenecen al módulo que las usa, no al módulo que las implementa. UserRepository se declara en la capa de dominio y se implementa en la capa de datos. Esta es la regla clave de DIP: el propietario de la abstracción es el consumidor, no el proveedor de la implementación.

¿Cómo afecta DIP a las pruebas?

DIP hace posible las pruebas en capas aisladas. Un ViewModel que depende de UserRepository (interfaz) se puede probar con una implementación mock sin base de datos. Sin DIP, el ViewModel dependería de RoomUserRepository y requeriría configurar la base de datos para cada prueba. DIP + DI proporcionan un aislamiento completo de módulos durante las pruebas.

¿Qué framework DI elegir para Android?

Hilt es la opción estándar para proyectos Android, recomendada por Google. Koin es una alternativa para proyectos Kotlin Multiplatform. Dagger 2 es para proyectos existentes donde la migración a Hilt no está justificada. Elegir un framework no elimina la necesidad de seguir DIP a nivel arquitectónico.

Resumen

  • DIP (Dependency Inversion Principle) — el quinto principio SOLID sobre límites arquitectónicos mediante abstracciones
  • Los módulos de alto nivel no dependen de módulos de bajo nivel — ambos dependen de abstracciones
  • DIP ≠ DI: principio vs patrón de implementación; DI es la herramienta, DIP es el objetivo
  • Las abstracciones pertenecen al consumidor (capa de dominio), no al proveedor (capa de datos)
  • Raíz de composición — el único lugar en la aplicación donde se ensamblan las dependencias concretas
  • Hilt, Koin, Dagger — herramientas DI que automatizan la conexión pero no reemplazan la decisión arquitectónica de DIP
  • La capa de dominio construida según DIP permanece independiente de frameworks, UI y bibliotecas de infraestructura

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