Event Bus y el patrón Observer en TypeScript
El patrón Observer (Observador) y su implementación práctica — Event Bus (Bus de eventos) — son herramientas fundamentales para construir aplicaciones débilmente acopladas y escalables. En este artículo, analizaremos en detalle cómo implementar un Event Bus en TypeScript, utilizando tipificación estricta, y veremos su aplicación en proyectos educativos.
¿Qué es el patrón Observer?
El patrón Observer define una relación de "uno a muchos" entre objetos. Cuando un objeto (editor, subject) cambia su estado, todos los objetos que dependen de él (observadores, observers) son notificados y actualizados automáticamente. Esto permite evitar un acoplamiento rígido entre componentes.
¿Qué es Event Bus?
Event Bus (bus de eventos) es un mecanismo centralizado que implementa el patrón Observer. En lugar de suscribir observadores directamente al editor, todos los componentes se comunican a través de un bus único. Un componente puede:
- Suscribirse (subscribe/listen) a un evento específico.
- Publicar (emit/publish) un evento, que será transmitido a todos los suscriptores.
- Cancelar la suscripción (unsubscribe) de un evento cuando ya no sea necesario.
Este enfoque es ideal para:
- La interacción entre módulos que no deben conocerse entre sí.
- El manejo de acciones del usuario (clics, entrada de datos).
- La reacción a cambios en el estado de la aplicación (carga de datos, errores).
Implementación de Event Bus en TypeScript
TypeScript añade un potente sistema de tipos, lo que hace que Event Bus sea seguro y predecible. Crearemos un bus tipificado, donde cada evento tiene un conjunto de argumentos estrictamente definido.
1. Definición de tipos de eventos
Primero, definimos una interfaz para mapear los nombres de los eventos y sus argumentos:
// Definimos un mapa de eventos: clave — nombre del evento, valor — tipo de argumentosinterface EventMap { 'user:login': { userId: string; timestamp: number }; 'data:loaded': { items: string[] }; 'error:occurred': { message: string; code: number }; // Se pueden añadir cualquier otro evento}2. Creación de la clase EventBus
Ahora creamos la clase en sí, usando genéricos para una tipificación estricta:
type Listener<T> = (payload: T) => void;
class EventBus<T extends Record<string, unknown>> { private listeners: Map<keyof T, Set<Listener<unknown>>> = new Map();
// Suscripción a un evento subscribe<K extends keyof T>(event: K, listener: Listener<T[K]>): () => void { if (!this.listeners.has(event)) { this.listeners.set(event, new Set()); } const currentListeners = this.listeners.get(event)!; currentListeners.add(listener as Listener<unknown>);
// Devolvemos una función para cancelar la suscripción return () => { currentListeners.delete(listener as Listener<unknown>); }; }
// Publicación de un evento emit<K extends keyof T>(event: K, payload: T[K]): void { const currentListeners = this.listeners.get(event); if (currentListeners) { currentListeners.forEach((listener) => { listener(payload); }); } }
// Cancelar suscripción de todos los eventos (útil para limpieza) clear(): void { this.listeners.clear(); }}
// Creamos una instancia del bus con nuestro mapa de eventosexport const appEventBus = new EventBus<EventMap>();3. Ejemplo de uso
Veamos cómo diferentes módulos pueden interactuar a través de Event Bus:
// Módulo de autenticaciónclass AuthService { login(userId: string) { // ... lógica de inicio de sesión ... appEventBus.emit('use También en la biblioteca
Generación de Swagger/OpenAPI para JavaScript (Node.js) — herramientas y bibliotecas
Colas de tareas en Node.js: Bull, Bee-Queue y Kue
Cliente Elasticsearch para Java: guía completa para trabajar con Java Client
iTextSharp para C# (.NET): guía completa con ejemplos
Trabajo con CSV en Kotlin: bibliotecas, análisis y escritura de datos