Decoradores e Inyección de Dependencias en Node.js

Clase 7 de 26Curso de Node.js Avanzado

Resumen

El uso de patrones como decorators e inyección de dependencias es actualmente esencial en el desarrollo moderno con JavaScript, especialmente en frameworks como Fastify y Next.js. Entender cómo implementar estos patrones permite crear aplicaciones más ordenadas y flexibles, extendiendo funcionalidades de forma dinámica y modular.

¿Qué son los decorators en JavaScript?

El patrón decorator es una técnica utilizada para añadir funcionalidad adicional a objetos ya existentes. En Node.js, y particularmente con Fastify, este patrón permite extender de forma dinámica los objetos, por ejemplo, el objeto request o response.

La principal ventaja de este patrón es que puedes agregar funciones personalizadas al objeto en cualquier parte del ciclo de vida de una petición, logrando mayor modularidad y dinamismo en el desarrollo de tus aplicaciones.

¿Qué es la inyección de dependencias y cómo funciona?

La inyección de dependencias es otro patrón clave en el desarrollo moderno, especialmente popularizado por frameworks como Next.js. La idea detrás de este patrón es desacoplar componentes y funcionalidades dentro de una aplicación, permitiéndote manejar dependencias externas de manera eficiente y escalable.

En Next.js, este patrón se ejemplifica claramente en proveedores de seguridad, donde diversas implementaciones son inyectadas en la aplicación según las necesidades específicas. Este método fomenta la separación de responsabilidades y facilita la gestión de cambios en el código.

¿Cómo combinas decorators con inyección de dependencias?

Combinar estos dos patrones puede incrementar significativamente la capacidad de organización y escalabilidad en tu aplicación. Un ejemplo práctico de cómo implementar y combinar ambos patrones en JavaScript es el siguiente:

Creación de la clase base

Primero, definimos una clase base denominada DataService que realizará una operación básica con datos:

class DataService {
  processData(data) {
    return data.map(item => item * 2);
  }
}

Implementación del decorator

Luego, creamos un decorator para añadir automáticamente logging. El decorator, en este ejemplo DataServiceWithLogging, añade funcionalidad sin modificar el comportamiento original:

class DataServiceWithLogging {
  constructor(dataService, logger) {
    this.dataService = dataService;
    this.logger = logger;
  }

  processData(data) {
    this.logger.log('iniciando procesamiento');
    const resultado = this.dataService.processData(data);
    this.logger.log('finalizando procesamiento');
    return resultado;
  }
}

Definición e inyección del logger

Luego definimos un objeto logger sencillo que implementará la dependencia que se va a inyectar, en este caso un logger que imprime mensajes en consola:

class Logger {
  log(message) {
    console.log('logger:', message);
  }
}

Uso del decorator con inyección de dependencias

Finalmente, utilizamos nuestro servicio decorado e inyectamos el logger mediante las instancias creadas:

const baseService = new DataService();
const logger = new Logger();
const decoratedService = new DataServiceWithLogging(baseService, logger);

const inputData = [1, 2, 3, 4];
const processedData = decoratedService.processData(inputData);
console.log('resultado procesado:', processedData);

Este ejemplo muestra claramente la sinergia de ambos patrones, generando código organizado, fácil de mantener y extensible. Los decorators aumentan funcionalidad sin alterar la esencia original, mientras que la inyección de dependencias asegura una gestión más sencilla y efectiva de componentes y funciones dentro del proyecto.

¿Te ha resultado útil este ejemplo práctico de decorators e inyección de dependencias? ¡Comparte tus comentarios y experiencias!