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



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                    

Recommendations