UNPKG

nestjs-event-sourcing-lib

Version:

A comprehensive Event Sourcing and CQRS library for NestJS applications

227 lines 8.37 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var EventBus_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.InMemoryEventStore = exports.EventBus = exports.EventPublishError = void 0; const common_1 = require("@nestjs/common"); /** * Event publish error */ class EventPublishError extends Error { constructor(message, eventId, eventType, originalError) { super(`Event publish failed: ${message}`); this.eventId = eventId; this.eventType = eventType; this.originalError = originalError; this.name = 'EventPublishError'; } } exports.EventPublishError = EventPublishError; /** * Local Event Bus for publishing and handling events */ let EventBus = EventBus_1 = class EventBus { constructor() { this.logger = new common_1.Logger(EventBus_1.name); this.handlers = new Map(); this.globalHandlers = []; } /** * Publishes single event */ async publish(event) { const startTime = Date.now(); try { this.logger.debug(`Publishing event ${event.eventType} for aggregate ${event.aggregateId}`, { eventId: event.id, aggregateId: event.aggregateId }); const metadata = this.createEventMetadata(event); const eventWrapper = { event, metadata }; const typeHandlers = this.handlers.get(event.eventType) || []; const allHandlers = [...typeHandlers, ...this.globalHandlers]; if (allHandlers.length === 0) { this.logger.debug(`No handlers found for event ${event.eventType}`, { eventId: event.id }); return; } const handlerPromises = allHandlers.map(handler => this.executeHandler(handler, event)); await Promise.allSettled(handlerPromises); const executionTime = Date.now() - startTime; this.logger.debug(`Event ${event.eventType} published successfully in ${executionTime}ms`, { eventId: event.id, handlersCount: allHandlers.length, executionTime }); } catch (error) { const executionTime = Date.now() - startTime; this.logger.error(`Failed to publish event ${event.eventType} after ${executionTime}ms`, { eventId: event.id, aggregateId: event.aggregateId, error: error instanceof Error ? error.message : String(error), executionTime, }); throw new EventPublishError(`Failed to publish event ${event.eventType}`, event.id, event.eventType, error instanceof Error ? error : new Error(String(error))); } } /** * Publishes multiple events */ async publishAll(events) { if (events.length === 0) { return; } const startTime = Date.now(); try { this.logger.debug(`Publishing ${events.length} events`, { eventsCount: events.length }); // Publish all events in parallel const publishPromises = events.map(event => this.publish(event)); await Promise.all(publishPromises); const executionTime = Date.now() - startTime; this.logger.debug(`${events.length} events published successfully in ${executionTime}ms`, { eventsCount: events.length, executionTime }); } catch (error) { const executionTime = Date.now() - startTime; this.logger.error(`Failed to publish ${events.length} events after ${executionTime}ms`, { eventsCount: events.length, error: error instanceof Error ? error.message : String(error), executionTime, }); throw error; } } /** * Subscribes to events of specific type */ subscribe(eventType, handler) { if (!this.handlers.has(eventType)) { this.handlers.set(eventType, []); } const handlers = this.handlers.get(eventType); handlers.push(handler); this.logger.debug(`Handler subscribed to event type ${eventType}`, { eventType, handlersCount: handlers.length }); } /** * Unsubscribes from events of specific type */ unsubscribe(eventType, handler) { const handlers = this.handlers.get(eventType); if (handlers) { const index = handlers.indexOf(handler); if (index !== -1) { handlers.splice(index, 1); this.logger.debug(`Handler unsubscribed from event type ${eventType}`, { eventType, handlersCount: handlers.length }); // Remove event type if no more handlers if (handlers.length === 0) { this.handlers.delete(eventType); } } } } /** * Subscribes to all events (global handler) */ subscribeToAll(handler) { this.globalHandlers.push(handler); this.logger.debug(`Global handler subscribed`, { globalHandlersCount: this.globalHandlers.length }); } /** * Unsubscribes from all events */ unsubscribeFromAll(handler) { const index = this.globalHandlers.indexOf(handler); if (index !== -1) { this.globalHandlers.splice(index, 1); this.logger.debug(`Global handler unsubscribed`, { globalHandlersCount: this.globalHandlers.length }); } } /** * Gets handler count for event type */ getHandlersCount(eventType) { return (this.handlers.get(eventType) || []).length; } /** * Gets global handlers count */ getGlobalHandlersCount() { return this.globalHandlers.length; } /** * Clears all handlers */ clear() { this.handlers.clear(); this.globalHandlers.length = 0; this.logger.debug('All event handlers cleared'); } /** * Executes event handler with error handling */ async executeHandler(handler, event) { try { await handler.handle(event); } catch (error) { this.logger.error(`Event handler failed for event ${event.eventType}`, { eventId: event.id, eventType: event.eventType, handlerName: handler.constructor.name, error: error instanceof Error ? error.message : String(error), }); } } /** * Creates metadata for event */ createEventMetadata(event) { return { eventId: event.id, aggregateId: event.aggregateId, eventType: event.eventType, version: event.version, timestamp: event.timestamp, }; } }; exports.EventBus = EventBus; exports.EventBus = EventBus = EventBus_1 = __decorate([ (0, common_1.Injectable)() ], EventBus); /** * Simple event handler that stores events in memory * Useful for testing and development */ let InMemoryEventStore = class InMemoryEventStore { constructor() { this.events = []; } async save(event) { this.events.push(event); } async saveAll(events) { this.events.push(...events); } getAll() { return [...this.events]; } getByType(eventType) { return this.events.filter(event => event.eventType === eventType); } getByAggregateId(aggregateId) { return this.events.filter(event => event.aggregateId === aggregateId); } clear() { this.events.length = 0; } count() { return this.events.length; } }; exports.InMemoryEventStore = InMemoryEventStore; exports.InMemoryEventStore = InMemoryEventStore = __decorate([ (0, common_1.Injectable)() ], InMemoryEventStore); //# sourceMappingURL=event.publisher.js.map