La inyección de dependencias (DI, por sus siglas en inglés) es un patrón de diseño ampliamente utilizado en el desarrollo de software para mejorar la modularidad y la facilidad de prueba de las aplicaciones. En el ecosistema de Node.js con TypeScript, una de las mejores opciones para implementar este patrón es InversifyJS.
InversifyJS es un contenedor de inyección de dependencias para JavaScript y TypeScript que sigue los principios de diseño SOLID. Algunas de sus características clave son:
✅ Soporte para decoradores de TypeScript.
✅ Compatible con arquitectura modular y escalable.
✅ Facilita la implementación del principio Inversión de Dependencias (D en SOLID).
✅ Integración sencilla con frameworks como Express.
Para comenzar, necesitamos instalar las dependencias necesarias en nuestro proyecto Node.js con TypeScript.
Ejecuta el siguiente comando:
npm install inversify reflect-metadata
También necesitamos instalar los tipos de TypeScript para Inversify:
npm install --save-dev @types/inversify
Adicionalmente, asegúrate de que tu archivo tsconfig.json tenga habilitada la opción "experimentalDecorators"
y "emitDecoratorMetadata"
:
{
"compilerOptions": {
"target": "ES6",
"module": "CommonJS",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Vamos a construir una estructura básica utilizando InversifyJS para gestionar dependencias en una aplicación Node.js con TypeScript.
Siguiendo las mejores prácticas de programación orientada a interfaces, primero creamos una interfaz para nuestro servicio:
export interface IUserService {
getUser(id: number): string;
}
Ahora creamos una clase que implementa la interfaz IUserService
:
import { injectable } from "inversify";
@injectable()
export class UserService implements IUserService {
getUser(id: number): string {
return `Usuario con ID: ${id}`;
}
}
📌 Nota: La anotación @injectable()
indica que esta clase puede ser gestionada por InversifyJS.
Inversify utiliza un contenedor de inyección de dependencias para gestionar las instancias de nuestras clases. Vamos a configurarlo:
import { Container } from "inversify";
import { IUserService } from "./interfaces/IUserService";
import { UserService } from "./services/UserService";
const container = new Container();
container.bind<IUserService>("IUserService").to(UserService);
export { container };
🔹 Aquí estamos registrando UserService
como la implementación de la interfaz IUserService
.
Ahora creamos un controlador que utilice UserService
mediante la inyección de dependencias:
import { inject, injectable } from "inversify";
import { IUserService } from "../interfaces/IUserService";
@injectable()
export class UserController {
private userService: IUserService;
constructor(@inject("IUserService") userService: IUserService) {
this.userService = userService;
}
getUser(id: number): string {
return this.userService.getUser(id);
}
}
📌 Explicación:
@injectable()
: Permite que la clase sea gestionada por Inversify.
@inject("IUserService")
: Especifica la inyección de la dependencia IUserService
.
Ahora usamos el contenedor de Inversify para obtener una instancia del UserController
:
import { container } from "./inversify.config";
import { UserController } from "./controllers/UserController";
const userController = container.get<UserController>(UserController);
console.log(userController.getUser(1)); // Usuario con ID: 1
🎉 ¡Listo! Ahora nuestra aplicación gestiona dependencias de forma automática y escalable.
Si queremos usar Inversify con Express, podemos hacerlo con el paquete inversify-express-utils
.
📌 Instalación:
npm install inversify-express-utils
📌 Configuración del Servidor:
import "reflect-metadata";
import express from "express";
import { InversifyExpressServer } from "inversify-express-utils";
import { container } from "./inversify.config";
// Crear el servidor Express con Inversify
const server = new InversifyExpressServer(container);
const app = server.build();
app.listen(3000, () => console.log("Servidor corriendo en http://localhost:3000"));
📌 Creando un Controlador para Express:
import { controller, httpGet } from "inversify-express-utils";
import { inject } from "inversify";
import { IUserService } from "../interfaces/IUserService";
@controller("/users")
export class UserController {
private userService: IUserService;
constructor(@inject("IUserService") userService: IUserService) {
this.userService = userService;
}
@httpGet("/:id")
getUser(req: any): string {
const id = parseInt(req.params.id);
return this.userService.getUser(id);
}
}
📌 Probando con Postman o el Navegador:
Si visitamos http://localhost:3000/users/1
, deberíamos ver:
{
"user": "Usuario con ID: 1"
}
Usar InversifyJS con Node.js y TypeScript nos permite desarrollar aplicaciones modulares, escalables y fáciles de mantener. Al separar las dependencias y gestionarlas con un contenedor de inversión de control, podemos escribir código más limpio y testear nuestros componentes de manera eficiente.
Si estás construyendo una aplicación grande con TypeScript, definitivamente deberías considerar InversifyJS para gestionar la inyección de dependencias. 🚀
Jorge García
Fullstack developer