Event Bus y el patrón Observer en TypeScript: implementación y ejemplos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Event Bus y el patrón Observer en TypeScript: de la teoría a la práctica



El desarrollo web moderno requiere una arquitectura flexible y escalable. Dos patrones clave que ayudan a organizar la interacción entre componentes son Event Bus y Observer. En este artículo analizaremos su implementación en TypeScript, sus ventajas y escenarios prácticos de uso.



¿Qué es el patrón Observer?



El patrón Observer (Observador) es un patrón de diseño de comportamiento que define una dependencia de "uno a muchos" entre objetos. Cuando un objeto (publicador) cambia su estado, todos los objetos dependientes (suscriptores) son notificados y actualizados automáticamente.



Elementos principales del patrón:

  • Subject (Publicador) — objeto que rastrea los cambios y notifica a los suscriptores.
  • Observer (Observador) — objeto que se suscribe a los cambios del publicador.
  • Event Bus (Bus de eventos) — mecanismo centralizado que gestiona las suscripciones y el envío de eventos entre componentes.


Implementación de Event Bus en TypeScript



TypeScript ofrece excelentes capacidades para una tipificación estricta, lo que hace que la implementación de Event Bus sea segura y predecible. Veamos una implementación básica:



type EventHandler<T = any> = (payload: T) => void;

class EventBus { private listeners: Map<string, EventHandler[]> = new Map();

// Suscripción a un evento subscribe<T>(event: string, handler: EventHandler<T>): void { if (!this.listeners.has(event)) { this.listeners.set(event, []); } this.listeners.get(event)!.push(handler); }

// Cancelar suscripción a un evento unsubscribe<T>(event: string, handler: EventHandler<T>): void { const handlers = this.listeners.get(event); if (handlers) { this.listeners.set( event, handlers.filter(h => h !== handler) ); } }

// Publicación de un evento emit<T>(event: string, payload: T): void { const handlers = this.listeners.get(event); if (handlers) { handlers.forEach(handler => handler(payload)); } }

// Limpiar todas las suscripciones clear(): void { this.listeners.clear(); }}

export const eventBus = new EventBus();


Ejemplo de uso con eventos tipificados



Para mejorar la seguridad de tipos, se pueden definir interfaces de eventos:



// Definimos los tipos de eventosinterface AppEvents {  'user:login': { userId: string; timestamp: number };  'user:logout': { userId: string };  'data:update': { id: string; data: unknown };  'notification:show': { message: string; type: 'success' | 'error' | 'info' };}

// Creamos un bus tipificadoclass TypedEventBus { private listeners: Map<string, Function[]> = new Map();

on<K extends keyof AppEvents>( event: K, handler: (payload: AppEvents[K]) => void ): void { if (!this.listeners.has(event)) { this.listeners.set(event, []); } this.listeners.get(event)!.push(handler); }

emit<K extends keyof AppEvents>( event: K, payload: AppEvents[K] ): void { const handlers = this.listeners.get(event); if (handlers) { handlers.forEach(handler => handler(payload)); } }

off<K extends keyof AppEvents>( event: K, handler: (payload: AppEvents[K]) => void ): void { const handlers = this.listeners.get(event); if (handlers) { this.listeners.set( event, handlers.filter(h => h !== handler) ); } }}

export const typedBus = new TypedEventBus();


Ejemplo práctico: sistema de notificaciones



Consideremos un escenario real: la creación de un sistema de notificaciones utilizando Event Bus:

Recommendations