mcp-quiz-server
Version:
🧠AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.
279 lines (278 loc) • 10.5 kB
JavaScript
;
/**
* @fileoverview Event Bus Factory Implementation - Infrastructure Layer
* @version 1.0.0
* @since 2025-07-29
* @lastUpdated 2025-07-29
* @module EventBusFactory Infrastructure Implementation
* @description Factory for creating different types of event bus implementations
* with proper configuration and dependency injection support.
* @contributors Claude Code Agent
* @dependencies Event bus implementations, configuration
* @requirements REQ-ARCH-001 (Clean Architecture Infrastructure Layer)
* @testCoverage Unit tests for factory methods and configuration
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.eventBusFactory = exports.ConcreteEventBusFactory = void 0;
exports.createEventBus = createEventBus;
const InMemoryEventBus_1 = require("./InMemoryEventBus");
/**
* Concrete Event Bus Factory
*
* @description factory for creating event bus instances
* with different implementations and configurations. Supports
* dependency injection and configuration validation.
*
* @example
* ```typescript
* const factory = new ConcreteEventBusFactory();
*
* // Create in-memory event bus for development
* const devBus = factory.createInMemory({
* maxRetries: 3,
* enableDeadLetterQueue: true
* });
*
* // Create Redis event bus for production
* const prodBus = factory.createRedis({
* redisUrl: 'redis://localhost:6379',
* maxRetries: 5,
* enablePersistence: true
* });
* ```
*
* @since 2025-07-29
* @author Claude Code Agent
* @requirements REQ-ARCH-001 (Clean Architecture Infrastructure Layer)
*/
class ConcreteEventBusFactory {
constructor() {
this.defaultConfig = {
maxRetries: 3,
retryDelayMs: 1000,
enableDeadLetterQueue: true,
maxConcurrency: 10,
enablePersistence: false,
eventTimeoutMs: 30000,
};
}
/**
* Get singleton instance
*/
static getInstance() {
if (!ConcreteEventBusFactory.instance) {
ConcreteEventBusFactory.instance = new ConcreteEventBusFactory();
}
return ConcreteEventBusFactory.instance;
}
/**
* Create an in-memory event bus
*/
createInMemory(config) {
const mergedConfig = this.mergeWithDefaults(config);
this.validateConfig(mergedConfig);
const eventBus = new InMemoryEventBus_1.InMemoryEventBus(mergedConfig);
// Add default middleware for logging and monitoring
this.addDefaultMiddleware(eventBus);
return eventBus;
}
/**
* Create a Redis-based event bus
*/
createRedis(config) {
const mergedConfig = this.mergeWithDefaults(config);
this.validateConfig(mergedConfig);
this.validateRedisConfig(config);
// For now, return in-memory implementation
// In a real implementation, this would create a Redis-backed event bus
console.warn('Redis event bus not implemented yet, falling back to in-memory');
return this.createInMemory(mergedConfig);
}
/**
* Create an Azure Service Bus event bus
*/
createAzureServiceBus(config) {
const mergedConfig = this.mergeWithDefaults(config);
this.validateConfig(mergedConfig);
this.validateAzureConfig(config);
// For now, return in-memory implementation
// In a real implementation, this would create an Azure Service Bus-backed event bus
console.warn('Azure Service Bus event bus not implemented yet, falling back to in-memory');
return this.createInMemory(mergedConfig);
}
/**
* Create a custom event bus implementation
*/
createCustom(implementation) {
if (!implementation) {
throw new Error('Custom implementation is required');
}
// Validate that the implementation conforms to the interface
this.validateEventBusImplementation(implementation);
return implementation;
}
/**
* Create event bus based on environment configuration
*/
createFromEnvironment() {
const eventBusType = process.env.EVENT_BUS_TYPE || 'in-memory';
switch (eventBusType.toLowerCase()) {
case 'in-memory':
return this.createInMemory(this.getConfigFromEnvironment());
case 'redis':
const redisUrl = process.env.REDIS_URL;
if (!redisUrl) {
throw new Error('REDIS_URL environment variable is required for Redis event bus');
}
return this.createRedis({
...this.getConfigFromEnvironment(),
redisUrl,
});
case 'azure':
const connectionString = process.env.AZURE_SERVICE_BUS_CONNECTION_STRING;
if (!connectionString) {
throw new Error('AZURE_SERVICE_BUS_CONNECTION_STRING environment variable is required');
}
return this.createAzureServiceBus({
...this.getConfigFromEnvironment(),
connectionString,
});
default:
throw new Error(`Unsupported event bus type: ${eventBusType}`);
}
}
/**
* Private helper methods
*/
mergeWithDefaults(config) {
return {
...this.defaultConfig,
...config,
};
}
validateConfig(config) {
if (config.maxRetries !== undefined && config.maxRetries < 0) {
throw new Error('maxRetries must be non-negative');
}
if (config.retryDelayMs !== undefined && config.retryDelayMs < 0) {
throw new Error('retryDelayMs must be non-negative');
}
if (config.maxConcurrency !== undefined && config.maxConcurrency < 1) {
throw new Error('maxConcurrency must be at least 1');
}
if (config.eventTimeoutMs !== undefined && config.eventTimeoutMs < 1000) {
throw new Error('eventTimeoutMs must be at least 1000ms');
}
}
validateRedisConfig(config) {
if (!config.redisUrl || typeof config.redisUrl !== 'string') {
throw new Error('Valid Redis URL is required');
}
// Basic URL validation
try {
new URL(config.redisUrl);
}
catch (error) {
throw new Error('Invalid Redis URL format');
}
}
validateAzureConfig(config) {
if (!config.connectionString || typeof config.connectionString !== 'string') {
throw new Error('Valid Azure Service Bus connection string is required');
}
// Basic connection string validation
if (!config.connectionString.includes('Endpoint=') ||
!config.connectionString.includes('SharedAccessKeyName=')) {
throw new Error('Invalid Azure Service Bus connection string format');
}
}
validateEventBusImplementation(implementation) {
const requiredMethods = [
'publish',
'publishMany',
'subscribe',
'subscribeToMany',
'unsubscribe',
'unsubscribeAll',
'clear',
'getStats',
'start',
'stop',
'isRunning',
'flush',
];
for (const method of requiredMethods) {
if (typeof implementation[method] !== 'function') {
throw new Error(`Custom event bus implementation missing required method: ${method}`);
}
}
}
getConfigFromEnvironment() {
return {
maxRetries: parseInt(process.env.EVENT_BUS_MAX_RETRIES || '3'),
retryDelayMs: parseInt(process.env.EVENT_BUS_RETRY_DELAY_MS || '1000'),
enableDeadLetterQueue: process.env.EVENT_BUS_ENABLE_DLQ !== 'false',
maxConcurrency: parseInt(process.env.EVENT_BUS_MAX_CONCURRENCY || '10'),
enablePersistence: process.env.EVENT_BUS_ENABLE_PERSISTENCE === 'true',
eventTimeoutMs: parseInt(process.env.EVENT_BUS_TIMEOUT_MS || '30000'),
};
}
addDefaultMiddleware(eventBus) {
// Add logging middleware
eventBus.addMiddleware({
async beforePublish(event, options) {
console.debug(`[EventBus] Publishing event: ${event.constructor.name}`, {
eventId: event.eventId,
occurredOn: event.occurredOn,
});
return event;
},
async beforeHandle(event, handler) {
console.debug(`[EventBus] Handling event: ${event.constructor.name} with ${handler.constructor.name}`);
},
async afterHandle(event, handler, result) {
console.debug(`[EventBus] Event handled successfully: ${event.constructor.name}`);
},
async onError(event, handler, error) {
console.error(`[EventBus] Event handling failed: ${event.constructor.name}`, {
handlerName: handler.constructor.name,
error: error.message,
eventId: event.eventId,
});
},
});
// Add performance monitoring middleware
eventBus.addMiddleware({
async beforeHandle(event, handler) {
event._startTime = Date.now();
},
async afterHandle(event, handler, result) {
const duration = Date.now() - (event._startTime || 0);
console.debug(`[EventBus] Event processing time: ${duration}ms`, {
eventType: event.constructor.name,
handlerName: handler.constructor.name,
});
},
});
}
}
exports.ConcreteEventBusFactory = ConcreteEventBusFactory;
function createEventBus(type, config) {
const factory = ConcreteEventBusFactory.getInstance();
switch (type) {
case 'in-memory':
return factory.createInMemory(config);
case 'redis':
return factory.createRedis(config);
case 'azure':
return factory.createAzureServiceBus(config);
case 'environment':
return factory.createFromEnvironment();
default:
throw new Error(`Unsupported event bus type: ${type}`);
}
}
/**
* Export singleton factory instance
*/
exports.eventBusFactory = ConcreteEventBusFactory.getInstance();