Contenedor de Inyección de Dependencias para JavaScript (Node.js) — Awilix
En el desarrollo moderno con Node.js, la limpieza de la arquitectura y la testabilidad del código pasan a primer plano. Una de las herramientas clave para lograr estos objetivos es la inyección de dependencias (Dependency Injection, DI). La biblioteca Awilix proporciona un contenedor DI potente, eficiente y flexible para JavaScript y TypeScript, ideal para aplicaciones de servidor con Node.js. En este artículo, analizaremos en detalle cómo usar Awilix para gestionar dependencias, mejorar la modularidad y simplificar las pruebas.
¿Qué es la Inyección de Dependencias y para qué sirve un contenedor?
La Inyección de Dependencias es un patrón de diseño en el que un objeto recibe sus dependencias desde el exterior, en lugar de crearlas él mismo. Esto implementa el principio de Inversión de Control (IoC). Un contenedor es una herramienta que automatiza el proceso de creación y suministro de estas dependencias. En lugar de crear manualmente instancias de clases y pasarlas entre sí, usted describe las reglas de construcción en el contenedor, y él mismo resuelve todas las cadenas de dependencias.
Principales ventajas de usar un contenedor DI:
- Reducción del acoplamiento del código: Los módulos dependen de abstracciones, no de implementaciones concretas.
- Simplificación de las pruebas: Es fácil reemplazar servicios reales por mocks durante las pruebas unitarias.
- Gestión centralizada: Toda la configuración de dependencias se encuentra en un solo lugar, lo que facilita la refactorización.
- Gestión del ciclo de vida: El contenedor puede controlar cuánto tiempo vive un objeto: una única instancia para toda la aplicación (singleton) o una nueva en cada solicitud.
Instalación de Awilix
Para empezar, instale la biblioteca a través de npm:
npm install awilixAwilix no tiene dependencias externas y funciona tanto con módulos CommonJS como ESM.
Conceptos principales de Awilix
Awilix se basa en varios conceptos clave:
- Container (contenedor): El objeto principal que almacena y proporciona las dependencias.
- Registration (registro): El proceso de vincular un nombre (token) con una fábrica, clase o valor.
- Resolver (resolvedor): Un objeto que determina cómo crear u obtener la dependencia. Por ejemplo,
asClass(),asFunction(),asValue(). - Lifetime (tiempo de vida): Indica cuántas veces se creará el objeto:
ScopedLifeTime(una vez por ámbito),SingletonLifeTime(una vez durante toda la ejecución) oTransientLifeTime(una nueva cada vez).
Ejemplo de uso: servicio para trabajar con usuarios
Consideremos un ejemplo sencillo. Supongamos que tenemos una base de datos, un repositorio de usuarios y un servicio para la lógica de negocio.
1. Definimos las clases
// database.jsclass Database { constructor() { this.connection = 'Connected to DB'; } query(sql) { return `Result for: ${sql}`; }}
// userRepository.jsclass UserRepository { constructor({ database }) { this.db = database; } findById(id) { return this.db.query(`SELECT * FROM users WHERE id = ${id}`); }}
// userService.jsclass UserService { constructor({ userRepository }) { this.repo = userRepository; } getUser(id) { return this.repo.findById(id); }}Observe los constructores: reciben un objeto con las dependencias. Esta es una práctica estándar para Awilix.
2. Configuración del contenedor
const { createContainer, asClass, asValue, Lifetime } = require('awilix');
const container = createContainer();
// Registramos la También en la biblioteca
Procesamiento de JSON en PHP: funciones, ejemplos y mejores prácticas
Rate Limiting en Swift: gestión de la frecuencia de solicitudes en iOS/macOS
ORM para Swift (iOS/macOS): comparación y ejemplos de uso
Generación de Swagger/OpenAPI para Node.js (JavaScript): herramientas y ejemplos