@jamx-framework/events
v1.0.0
Published
JAMX Framework — Typed event bus
Downloads
116
Maintainers
Readme
@jamx-framework/events
Descripción
Sistema de eventos (Event Bus) para JAMX Framework. Proporciona un mecanismo de comunicación desacoplada entre componentes de la aplicación mediante publicación/suscripción (pub/sub). Permite que diferentes partes de la aplicación se comuniquen sin tener dependencias directas, ideal para arquitecturas modulares y microservicios.
Cómo funciona
El EventBus implementa el patrón pub/sub:
- Registro de listeners: Los componentes se suscriben a eventos específicos con un callback.
- Emisión de eventos: Cualquier componente puede publicar un evento en el bus.
- Distribución: El bus invoca todos los listeners registrados para ese tipo de evento.
- Filtrado: Soporta filtros por tipo de evento y datos adicionales.
- Async por defecto: Los listeners pueden ser síncronos o asíncronos (retornan Promise).
Componentes principales
- src/event-bus.ts: Clase
EventBusque gestiona suscripciones y emisión - src/index.ts: Punto de exportación
Uso básico
import { EventBus } from '@jamx-framework/events';
// Crear instancia global
const bus = new EventBus();
// Suscribirse a un evento
bus.on('user.created', (data) => {
console.log('Usuario creado:', data.userId);
});
// Suscribirse una sola vez
bus.once('order.completed', (data) => {
console.log('Orden completada:', data.orderId);
});
// Emitir evento
bus.emit('user.created', { userId: 123, email: '[email protected]' });
// Emitir evento async
await bus.emitAsync('order.processing', { orderId: 456 });Ejemplos
Comunicación entre módulos
// En módulo de usuarios
bus.emit('user.registered', { userId: 1, email: '[email protected]' });
// En módulo de notificaciones
bus.on('user.registered', async (data) => {
await mailer.sendWelcomeEmail(data.email);
});
// En módulo de analytics
bus.on('user.registered', (data) => {
analytics.track('signup', { userId: data.userId });
});Eventos con datos estructurados
// Definir tipo para evento
interface UserCreatedEvent {
userId: number;
email: string;
timestamp: Date;
}
// Emitir con tipo
bus.emit<UserCreatedEvent>('user.created', {
userId: 1,
email: '[email protected]',
timestamp: new Date(),
});
// Escuchar con tipo
bus.on('user.created', (event) => {
console.log(`Usuario ${event.userId} registrado a las ${event.timestamp}`);
});Manejo de errores en listeners
bus.on('payment.processed', async (data) => {
try {
await processPayment(data);
} catch (error) {
console.error('Error procesando pago:', error);
// Opcional: emitir evento de error
bus.emit('payment.error', { orderId: data.orderId, error });
}
});Remover listeners
// Suscribirse y guardar referencia
const handler = (data) => console.log(data);
bus.on('user.updated', handler);
// Remover listener específico
bus.off('user.updated', handler);
// Remover todos los listeners de un evento
bus.off('user.updated');
// Remover todos los listeners de todos los eventos
bus.removeAllListeners();Eventos con prioridad
// Añadir listener con prioridad (menor número = mayor prioridad)
bus.on('critical.event', (data) => {
// Se ejecuta primero
}, { priority: 1 });
bus.on('critical.event', (data) => {
// Se ejecuta después
}, { priority: 10 });Eventos con wildcard
// Suscribirse a todos los eventos de usuarios
bus.on('user.*', (eventName, data) => {
console.log(`Evento de usuario: ${eventName}`, data);
});
// Suscribirse a eventos de una jerarquía
bus.on('order.**', (eventName, data) => {
// Captura order.created, order.updated, order.cancelled, etc.
});Flujo interno
- Registro:
on(event, callback, options?)añade el callback a un array de listeners para ese evento. - Emisión:
emit(event, data)recorre todos los listeners del evento y los ejecuta en orden de registro (o prioridad). - Async: Si un listener retorna una Promise, se espera a que se resuelva (en
emitAsync). - Filtrado: Antes de ejecutar, se pueden aplicar filtros por tipo de evento o datos.
- Error handling: Los errores en listeners no detienen la ejecución de otros listeners (se capturan y se emiten opcionalmente en
errorevent). - Memory management:
off()yremoveAllListeners()permiten limpiar listeners para evitar memory leaks.
API Reference (Resumen)
EventBus
on(event: string, callback: Function, options?: ListenerOptions): () => voidonce(event: string, callback: Function, options?: ListenerOptions): () => voidoff(event: string, callback?: Function): voidemit(event: string, data?: any): voidemitAsync(event: string, data?: any): Promise<void>removeAllListeners(event?: string): voidlistenerCount(event: string): numbereventNames(): string[]
ListenerOptions
priority?: number— Orden de ejecución (default: 0)once?: boolean— Se ejecuta solo una vez (alternativa aonce())async?: boolean— Si se debe esperar a que termine (default: true)
Performance Considerations
- Listener lookup: Los listeners se almacenan en un Map por evento, O(1) para búsqueda.
- Async execution:
emitAsyncespera todos los listeners;emitno espera. - Wildcard matching: Los wildcards (
*,**) se evalúan con regex y pueden ser costosos con muchos listeners. - Memory leaks: Los listeners deben removerse explícitamente cuando el componente se destruye.
Testing
Tests en packages/events/tests/unit/event-bus.test.ts:
pnpm testCubre:
- Registro y desregistración de listeners
- Emisión síncrona y asíncrona
- Eventos con wildcard
- Prioridades de listeners
- Manejo de errores
- Conteo de listeners
Compatibility
- Compatible con Node.js 18+
- Funciona en navegadores (si se polyfill)
- No requiere dependencias externas
- Type-safe con genéricos
Use Cases
- Comunicación entre módulos: Desacoplar módulos que necesitan comunicarse
- Eventos de dominio: UserCreated, OrderCompleted, PaymentProcessed
- Hooks del sistema: beforeStart, afterInit, onShutdown
- Logging y auditoría: Capturar eventos para registro
- Reacción a cambios: Actualizar UI cuando cambian datos
Best Practices
- Usar nombres de evento consistentes:
module.action(ej:user.created,order.updated) - Estructurar datos de eventos: Objetos con campos claros y tipados
- Limpiar listeners: En componentes con ciclo de vida, remover listeners al destruir
- No abusar: Para comunicación directa entre clases cercanas, usa inyección de dependencias
- Documentar eventos: Mantener un registro de eventos que emite cada módulo
This event bus provides a lightweight, type-safe pub/sub system for JAMX applications, enabling loose coupling and reactive architectures with minimal overhead.
