Event Bus y el patrón Observer en JavaScript (Node.js)
En el desarrollo moderno con Node.js, a menudo surge la necesidad de organizar una interacción débilmente acoplada entre los componentes de una aplicación. Para resolver este problema, el patrón Observer y su implementación práctica — Event Bus — son ideales. En este artículo, analizaremos en detalle qué es Event Bus, cómo se relaciona con el patrón Observer y crearemos nuestra propia implementación en JavaScript puro para Node.js.
¿Qué es el patrón Observer?
El patrón Observer define una relación de "uno a muchos" entre objetos. Cuando un objeto (sujeto o editor) cambia su estado, todos los objetos que dependen de él (observadores o suscriptores) son notificados automáticamente. En Node.js, este patrón está integrado en el núcleo a través de la clase EventEmitter.
Componentes principales del patrón:
- Subject (Sujeto) — el objeto que genera eventos.
- Observer (Observador) — el objeto que se suscribe a los eventos.
- Subscription (Suscripción) — el mecanismo para registrar un observador en el sujeto.
¿Qué es Event Bus?
Event Bus es un mediador centralizado que implementa el patrón Observer. En lugar de que cada componente se suscriba directamente a otro componente, todos se comunican a través de un bus de eventos único. Esto proporciona las siguientes ventajas:
- Bajo acoplamiento entre componentes.
- Facilidad para agregar nuevos suscriptores sin modificar el código del editor.
- Posibilidad de gestionar eventos de forma centralizada (registro, filtrado, depuración).
EventEmitter integrado en Node.js
Node.js proporciona el módulo integrado events, que contiene la clase EventEmitter. Esta es una implementación lista de Event Bus. Veamos un ejemplo básico:
const EventEmitter = require('events');const eventBus = new EventEmitter();
// Nos suscribimos al evento 'user:login'eventBus.on('user:login', (user) => { console.log(`El usuario ${user.name} ha iniciado sesión`);});
// Nos suscribimos con otro manejadoreventBus.on('user:login', (user) => { console.log(`Enviando correo de bienvenida para ${user.email}`);});
// Generamos el eventoeventBus.emit('user:login', { name: 'Iván', email: 'ivan@example.com' });// Salida:// El usuario Iván ha iniciado sesión// Enviando correo de bienvenida para ivan@example.comCreación de un Event Bus propio
A veces el EventEmitter integrado no es suficiente — por ejemplo, si se necesita agregar soporte para manejadores asíncronos, prioridades o tipado estricto. Vamos a crear nuestra propia implementación de Event Bus desde cero:
class EventBus { constructor() { this.events = {}; }
// Suscripción a un evento on(eventName, callback) { if (!this.events[eventName]) { this.events[eventName] = []; } this.events[eventName].push(callback); // Devolvemos una función para cancelar la suscripción return () => { this.events[eventName] = this.events[eventName] .filter(cb => cb !== callback); }; }
// Suscripción a un evento de una sola vez once(eventName, callback) { const wrapper = (...args) => { callback(...args); this.off(eventName, wrapper); }; this.on(eventName, wrapper); }
// Generación de un evento emit(eventName, ...args) { const callbacks = this.events[eventName]; if (!callbacks) return false; callbacks.forEach(callback => { try { callback(...args); } catch (error) { console.error(`Error en el manejador del evento ${eventName}:`, error); } }); return true; También en la biblioteca
Bibliotecas WebSocket para Node.js: ws, Socket.IO y uWebSockets.js
Limitación de tasa en C++: bibliotecas y herramientas para control de solicitudes
Programación reactiva en Swift: Combine y RxSwift para iOS/macOS
nock para TypeScript: guía completa con ejemplos prácticos