nestjs-event-sourcing-lib
Version:
A comprehensive Event Sourcing and CQRS library for NestJS applications
227 lines • 8.37 kB
JavaScript
;
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