Inyección de Dependencias en JavaScript: guía completa para principiantes

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿Qué es la Inyección de Dependencias y para qué sirve?

\n\n

Imagine que está armando una computadora. En lugar de comprar un monitor todo en uno ya hecho, usted elige por separado el procesador, la tarjeta de video, la memoria RAM y los conecta a la placa base. Si desea actualizar la tarjeta de video, no tendrá que cambiar toda la computadora, simplemente reemplazará un componente. La Inyección de Dependencias (DI) en programación funciona bajo el mismo principio.

\n\n

La Inyección de Dependencias (Dependency Injection) es un patrón de diseño en el cual un objeto recibe sus dependencias desde el exterior, en lugar de crearlas por sí mismo. En el contexto de JavaScript y Node.js, esto significa que una clase o función no crea instancias de otras clases internamente, sino que las recibe a través del constructor o de parámetros.

\n\n

Este enfoque es parte del principio de Inversión de Dependencias (Dependency Inversion Principle) de SOLID y hace que el código sea más flexible, comprobable y mantenible. En este artículo, analizaremos la DI con ejemplos prácticos para desarrolladores principiantes.

\n\n

El problema: acoplamiento rígido del código

\n\n

Consideremos una situación típica en una aplicación Node.js. Supongamos que tenemos un servicio para enviar notificaciones por correo electrónico:

\n\n
// Sin Inyección de Dependencias\nclass EmailService {\n  sendEmail(to, subject, body) {\n    console.log(`Enviando correo a ${to}: ${subject}`);\n    // lógica real de envío\n  }\n}\n\nclass UserService {\n  constructor() {\n    this.emailService = new EmailService(); // ❌ Acoplamiento rígido\n  }\n\n  registerUser(email) {\n    // Lógica de registro de usuario\n    this.emailService.sendEmail(email, '¡Bienvenido!', '...');\n  }\n}\n\nconst userService = new UserService();\nuserService.registerUser('user@example.com');\n
\n\n

¿Cuál es el problema? La clase UserService está rígidamente vinculada a una implementación específica de EmailService. Si desea:

\n
    \n
  • Reemplazar el envío de correos por el envío de SMS
  • \n
  • Añadir registro (logging) de todos los mensajes enviados
  • \n
  • Probar UserService sin enviar correos reales
  • \n
\n\n

Tendrá que reescribir el código de UserService. Esto viola el principio de abierto/cerrado (Open/Closed Principle).

\n\n

Solución: inyección de dependencias a través del constructor

\n\n

La primera y más sencilla forma de inyección de dependencias es pasarlas a través del constructor de la clase. Reescribamos nuestro ejemplo:

\n\n
// Con Inyección de Dependencias\nclass EmailService {\n  sendEmail(to, subject, body) {\n    console.log(`Enviando correo a ${to}: ${subject}`);\n  }\n}\n\nclass SmsService {\n  sendSms(phone, message) {\n    console.log(`Enviando SMS a ${phone}: ${message}`);\n  }\n}\n\nclass UserService {\n  constructor(notificationService) {\n    this.notificationService = notificationService; // ✅ Inyección de dependencia\n  }\n\n  registerUser(contact, isPhone = false) {\n    // Lógica de registro\n    if (isPhone) {\n      this.notificationService.sendSms(contact, '¡Bienvenido!');\n    } else {\n      this.notificationService.sendEmail(contact, '¡Bienvenido!', '...');\n    }\n  }\n}\n\n// Uso\nconst emailService = new EmailService();\nconst userServiceWithEmail = new UserService(emailService);\nuserServiceWithEmail.registerUser('user@example.com');\n\nconst smsService = new SmsService();\nconst userServiceWithSms = new UserService(smsService);\nuserServiceWithSms.registerUser('+1234567890', true);\n
\n\n

Ahora UserService no sabe qué servicio específico se está utilizando — si correo o SMS. Simplemente llama al método sendEmail o sendSms del objeto que se le ha pasado. Esto se llama inversión de dependencias: el módulo de alto nivel no depende del módulo de bajo nivel, ambos dependen de una abstracción.

\n\n

Ventajas de este enfoque:

\n
    \n
  • Flexibilidad: es fácil reempla

Blogs

Book Recommendations