SOLIDPrincipios

Inversión de dependencias (DIP)

Los módulos de alto nivel no deben depender de los de bajo nivel; ambos deben depender de abstracciones.

¿Qué es?

El Dependency Inversion Principle dice que la lógica de negocio (alto nivel) no debería depender directamente de detalles de implementación concretos (bajo nivel), como una clase específica que envía emails o que habla con una base de datos. En vez de eso, ambos deberían depender de una abstracción (una interfaz), y la implementación concreta se "inyecta" desde afuera.

Antes: dependencia directa de una clase concreta

class EmailSender {
  send(to: string, message: string): void {
    console.log(`Email a ${to}: ${message}`);
  }
}

// El módulo de alto nivel depende directamente del detalle de bajo nivel
class NotificationService {
  private emailSender = new EmailSender();

  notify(user: string, message: string): void {
    this.emailSender.send(user, message);
  }
}

Si mañana hay que notificar por SMS o push en vez de (o además de) email, hay que modificar NotificationService directamente. Y para testear esta clase de forma aislada, es imposible reemplazar EmailSender por un mock sin tocar el código.

Después: se depende de una abstracción

interface MessageSender {
  send(to: string, message: string): void;
}

class EmailSender implements MessageSender {
  send(to: string, message: string): void {
    console.log(`Email a ${to}: ${message}`);
  }
}

class SmsSender implements MessageSender {
  send(to: string, message: string): void {
    console.log(`SMS a ${to}: ${message}`);
  }
}

// El módulo de alto nivel solo conoce la abstracción
class NotificationService {
  constructor(private sender: MessageSender) {}

  notify(user: string, message: string): void {
    this.sender.send(user, message);
  }
}

const service = new NotificationService(new SmsSender());
service.notify('+54 9 11 1234-5678', 'Tu pedido fue enviado');

NotificationService ya no sabe (ni le importa) si se manda un email o un SMS: recibe cualquier MessageSender por su constructor. Esto se llama inyección de dependencias, y también hace que sea trivial pasar un sender falso en los tests.

A tener en cuenta: "Inversión" se refiere a que, normalmente, el módulo de alto nivel definiría y controlaría al de bajo nivel; acá se invierte: ambos dependen de una interfaz definida en función del módulo de alto nivel, y el detalle concreto se adapta a ella.
inyección de dependenciasabstracciónDIP