Arquitectura Event-driven en TypeScript: cuando el código se «escucha» entre sí
\n\nImagina que estás escribiendo una aplicación web compleja: un mensajero, un bot de trading o un sistema de monitoreo de servidores. En algún momento te das cuenta: simplemente llamar funciones en cadena es un infierno. Un módulo arrastra a otro, el tercero depende del cuarto, y al cambiar la lógica todo se desmorona como un castillo de naipes.
\n\n¿Te suena familiar? Entonces necesitas la arquitectura Event-driven en TypeScript. No es solo un término de moda, sino una forma real de hacer que el código sea flexible, extensible y comprensible. En este artículo, analizaremos cómo funciona la programación orientada a eventos, escribiremos nuestro propio bus de eventos en TypeScript y veremos dónde realmente ayuda.
\n\nQué es la arquitectura Event-driven en palabras sencillas
\n\nLa arquitectura Event-driven (arquitectura orientada a eventos) es un enfoque en el que los componentes del sistema se comunican a través de eventos. Un módulo genera un evento (por ejemplo, «usuario registrado»), y otros se suscriben a él y reaccionan. Entre ellos no están directamente vinculados.
\n\n¿Suena abstracto? Pongamos un ejemplo de la vida real. Imagina una oficina:
\n- \n
- Enfoque tradicional: el jefe se acerca personalmente a cada empleado y le dice: «Haz el informe». \n
- Event-driven: el jefe grita: «¡Se necesita un informe!» — y todos los que saben hacerlo se ponen a trabajar. \n
En el segundo caso, el jefe no necesita saber quién hará exactamente el informe. Simplemente envía el evento. Y los empleados pueden conectarse y desconectarse sin cambiar la lógica del jefe. Esta es la esencia de la programación orientada a eventos.
\n\nPor qué TypeScript es ideal para la arquitectura de eventos
\n\nJavaScript en sí mismo está orientado a eventos (recuerda addEventListener), pero en proyectos grandes, sin tipos, comienza el caos. Los eventos en TypeScript resuelven este problema: sabes exactamente qué datos llegan en el evento, y el compilador detectará el error en la etapa de desarrollo, no en tiempo de ejecución.
Además, TypeScript admite genéricos, sobrecargas y tipado estricto de callbacks — todo lo necesario para crear un bus de eventos robusto.
\n\nEscribimos un Event Bus en TypeScript desde cero
\n\nEmpecemos con un ejemplo simple pero funcional. Crearemos la clase EventBus — un bus de eventos central a través del cual se comunicarán todos los módulos.
// event-bus.ts\ntype EventHandler<T = any> = (data: T) => void;\n\nclass EventBus {\n private handlers: Map<string, EventHandler[]> = new Map();\n\n // Suscripción a un evento\n on<T>(event: string, handler: EventHandler<T>): void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, []);\n }\n this.handlers.get(event)!.push(handler as EventHandler);\n }\n\n // Cancelar suscripción a un evento\n off(event: string, handler: EventHandler): void {\n const handlers = this.handlers.get(event);\n if (handlers) {\n this.handlers.set(event, handlers.filter(h => h !== handler));\n }\n }\n\n // Emisión de un evento\n emit<T>(event: string, data: T): void {\n const handlers = this.handlers.get(event);\n if (handlers) {\n handlers.forEach(handler => handler(data));\n }\n }\n}\n\nexport const globalEventBus = new EventBus();\n\nAhí lo tenemos, hemos escrito una arquitectura Event-driven en TypeScript simple. Ahora cualquier módulo puede importar globalEventBus y ya sea escuchar eventos o generarlos.
Ejemplo de uso: notificaciones y registro
\n\nImaginemos que tenemos un módulo de autenticación y un módulo de notificaciones. No deberían conocerse directamente entre sí.
\n\n// auth-module.ts\nimport { globalEventBus } from './event-bus';\n\nexport function loginUser(email: string): void {\n // ... lógica de inicio de sesión\n console.log(`Usu