Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Latest commit

 

History

History
1309 lines (1074 loc) · 32.4 KB

File metadata and controls

1309 lines (1074 loc) · 32.4 KB

Node Service - Microservices Patterns Implementation

Total Patterns: 32

1. Circuit Breaker Pattern

Theory: Prevents cascading failures by monitoring service calls and "opening" the circuit when failure rate exceeds threshold.

Code:

class NodeCircuitBreaker {
    constructor(options = {}) {
        this.failureThreshold = options.failureThreshold || 5;
        this.timeout = options.timeout || 60000;
        this.state = 'CLOSED';
        this.failureCount = 0;
        this.lastFailureTime = null;
    }

    async execute(operation) {
        if (this.state === 'OPEN') {
            if (Date.now() - this.lastFailureTime > this.timeout) {
                this.state = 'HALF_OPEN';
            } else {
                throw new Error('Circuit breaker is OPEN');
            }
        }

        try {
            const result = await operation();
            this.onSuccess();
            return result;
        } catch (error) {
            this.onFailure();
            throw error;
        }
    }
}

2. Retry Pattern

Theory: Automatically retries failed operations with configurable delays and maximum attempts.

Code:

class RetryService {
    async executeWithRetry(operation, options = {}) {
        const maxAttempts = options.maxAttempts || 3;
        const delay = options.delay || 1000;
        
        for (let attempt = 1; attempt <= maxAttempts; attempt++) {
            try {
                return await operation();
            } catch (error) {
                if (attempt === maxAttempts) throw error;
                await this.sleep(delay * Math.pow(2, attempt - 1));
            }
        }
    }
    
    sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}

3. Bulkhead Pattern

Theory: Isolates resources to prevent one failing component from affecting others.

Code:

class BulkheadService {
    constructor(options = {}) {
        this.maxConcurrent = options.maxConcurrent || 5;
        this.queue = [];
        this.running = 0;
    }

    async execute(operation) {
        return new Promise((resolve, reject) => {
            this.queue.push({ operation, resolve, reject });
            this.processQueue();
        });
    }

    async processQueue() {
        if (this.running >= this.maxConcurrent || this.queue.length === 0) {
            return;
        }

        this.running++;
        const { operation, resolve, reject } = this.queue.shift();

        try {
            const result = await operation();
            resolve(result);
        } catch (error) {
            reject(error);
        } finally {
            this.running--;
            this.processQueue();
        }
    }
}

4. Rate Limiting Pattern

Theory: Controls the rate of requests to prevent system overload.

Code:

class TokenBucketRateLimiter {
    constructor(options = {}) {
        this.capacity = options.capacity || 100;
        this.refillRate = options.refillRate || 10;
        this.tokens = this.capacity;
        this.lastRefill = Date.now();
    }

    isAllowed(clientId) {
        this.refill();
        
        if (this.tokens >= 1) {
            this.tokens--;
            return { allowed: true };
        }
        
        return { 
            allowed: false, 
            resetTime: Math.ceil((1 / this.refillRate) * 1000) 
        };
    }

    refill() {
        const now = Date.now();
        const timePassed = (now - this.lastRefill) / 1000;
        const tokensToAdd = timePassed * this.refillRate;
        
        this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);
        this.lastRefill = now;
    }
}

5. Cache-Aside Pattern

Theory: Application manages cache directly, loading data on cache miss.

Code:

class CacheAsideService {
    constructor() {
        this.cache = new Map();
        this.ttl = new Map();
    }

    async get(key, dataLoader, ttlMs = 300000) {
        if (this.isValid(key)) {
            return this.cache.get(key);
        }

        const data = await dataLoader();
        this.set(key, data, ttlMs);
        return data;
    }

    set(key, value, ttlMs) {
        this.cache.set(key, value);
        this.ttl.set(key, Date.now() + ttlMs);
    }

    isValid(key) {
        return this.cache.has(key) && Date.now() < this.ttl.get(key);
    }
}

6. Event Sourcing Pattern

Theory: Stores state changes as events, enabling audit trails and replay capabilities.

Code:

class EventStore {
    constructor() {
        this.events = [];
        this.snapshots = new Map();
    }

    append(event) {
        event.version = this.events.length + 1;
        event.timestamp = Date.now();
        this.events.push(event);
        return event;
    }

    getEvents(aggregateId, fromVersion = 0) {
        return this.events.filter(event => 
            event.aggregateId === aggregateId && event.version > fromVersion
        );
    }

    replay(aggregateId) {
        const events = this.getEvents(aggregateId);
        return events.reduce((state, event) => this.applyEvent(state, event), {});
    }
}

7. CQRS Pattern

Theory: Separates read and write operations for better scalability and performance.

Code:

class CommandHandler {
    constructor(eventStore, cache) {
        this.eventStore = eventStore;
        this.cache = cache;
    }

    async handle(command) {
        switch (command.type) {
            case 'PROCESS_DATA':
                return this.handleProcessData(command);
            default:
                throw new Error(`Unknown command type: ${command.type}`);
        }
    }

    async handleProcessData(command) {
        const result = await this.processData(command.payload);
        
        const event = {
            id: require('uuid').v4(),
            type: 'DATA_PROCESSED',
            aggregateId: command.id,
            data: { command: command.payload, result }
        };
        
        this.eventStore.append(event);
        return result;
    }
}

class QueryHandler {
    constructor(cache, readModel) {
        this.cache = cache;
        this.readModel = readModel;
    }

    async handle(query) {
        switch (query.type) {
            case 'GET_PROCESSED_DATA':
                return this.getProcessedData(query.parameters);
            default:
                throw new Error(`Unknown query type: ${query.type}`);
        }
    }
}

8. Saga Pattern

Theory: Manages distributed transactions across multiple services with compensation.

Code:

class SagaOrchestrator {
    constructor() {
        this.sagas = new Map();
        this.sagaDefinitions = new Map();
    }

    registerSaga(name, steps) {
        this.sagaDefinitions.set(name, steps);
    }

    async startSaga(sagaType, context) {
        const sagaId = require('uuid').v4();
        const saga = {
            id: sagaId,
            type: sagaType,
            status: 'STARTED',
            context,
            currentStep: 0,
            completedSteps: []
        };

        this.sagas.set(sagaId, saga);
        await this.executeSaga(sagaId);
        return saga;
    }

    async executeSaga(sagaId) {
        const saga = this.sagas.get(sagaId);
        const steps = this.sagaDefinitions.get(saga.type);

        try {
            for (let i = saga.currentStep; i < steps.length; i++) {
                const step = steps[i];
                const result = await step.execute(saga.context);
                
                saga.context = { ...saga.context, ...result };
                saga.completedSteps.push(step.name);
                saga.currentStep = i + 1;
            }
            
            saga.status = 'COMPLETED';
        } catch (error) {
            await this.compensate(saga);
            saga.status = 'COMPENSATED';
        }
    }
}

9. Outbox Pattern

Theory: Ensures reliable event publishing by storing events in the same transaction as business data.

Code:

class OutboxService {
    constructor() {
        this.outboxEvents = [];
        this.processing = false;
    }

    saveEvent(aggregateId, eventType, eventData) {
        const event = {
            id: require('uuid').v4(),
            aggregateId,
            eventType,
            eventData,
            timestamp: Date.now(),
            processed: false
        };
        
        this.outboxEvents.push(event);
        return event.id;
    }

    startProcessor() {
        if (this.processing) return;
        
        this.processing = true;
        setInterval(() => this.processEvents(), 5000);
    }

    async processEvents() {
        const unprocessedEvents = this.outboxEvents.filter(e => !e.processed);
        
        for (const event of unprocessedEvents) {
            try {
                await this.publishEvent(event);
                event.processed = true;
            } catch (error) {
                console.error('Failed to publish event:', error);
            }
        }
    }
}

10. Repository Pattern

Theory: Encapsulates data access logic and provides a uniform interface.

Code:

class InMemoryRepository {
    constructor() {
        this.data = new Map();
    }

    async save(entity) {
        const id = entity.id || require('uuid').v4();
        entity.id = id;
        this.data.set(id, { ...entity });
        return entity;
    }

    async findById(id) {
        return this.data.get(id) || null;
    }

    async findAll() {
        return Array.from(this.data.values());
    }

    async findBySpecification(specification) {
        const all = await this.findAll();
        return all.filter(item => specification.isSatisfiedBy(item));
    }
}

11. Specification Pattern

Theory: Encapsulates business rules in reusable specification objects.

Code:

class ValueGreaterThanSpecification {
    constructor(threshold) {
        this.threshold = threshold;
    }

    isSatisfiedBy(entity) {
        return entity.value > this.threshold;
    }

    and(other) {
        return new AndSpecification(this, other);
    }

    or(other) {
        return new OrSpecification(this, other);
    }
}

class AndSpecification {
    constructor(left, right) {
        this.left = left;
        this.right = right;
    }

    isSatisfiedBy(entity) {
        return this.left.isSatisfiedBy(entity) && this.right.isSatisfiedBy(entity);
    }
}

12. Feature Toggle Pattern

Theory: Enables/disables features at runtime without code deployment.

Code:

class FeatureToggle {
    constructor(features = {}) {
        this.features = features;
    }

    isEnabled(featureName, context = {}) {
        const feature = this.features[featureName];
        if (!feature || !feature.enabled) {
            return false;
        }

        if (feature.rolloutPercentage && feature.rolloutPercentage < 100) {
            const userId = context.userId || '';
            const hash = this.hashCode(featureName + userId) % 100;
            return hash < feature.rolloutPercentage;
        }

        return true;
    }

    hashCode(str) {
        let hash = 0;
        for (let i = 0; i < str.length; i++) {
            const char = str.charCodeAt(i);
            hash = ((hash << 5) - hash) + char;
            hash = hash & hash;
        }
        return Math.abs(hash);
    }
}

13. Health Check Pattern

Theory: Provides endpoints to monitor service health and dependencies.

Code:

class HealthCheckService {
    constructor() {
        this.checks = new Map();
    }

    addCheck(name, checkFunction, options = {}) {
        this.checks.set(name, {
            function: checkFunction,
            critical: options.critical || false,
            lastResult: null,
            lastCheck: null
        });
    }

    async runAllChecks() {
        const results = {};
        let overallStatus = 'UP';

        for (const [name, check] of this.checks) {
            try {
                const result = await check.function();
                check.lastResult = result;
                check.lastCheck = Date.now();
                results[name] = result;

                if (!result.healthy && check.critical) {
                    overallStatus = 'DOWN';
                } else if (!result.healthy && overallStatus === 'UP') {
                    overallStatus = 'DEGRADED';
                }
            } catch (error) {
                const errorResult = { healthy: false, error: error.message };
                results[name] = errorResult;
                check.lastResult = errorResult;
                
                if (check.critical) {
                    overallStatus = 'DOWN';
                }
            }
        }

        return {
            status: overallStatus,
            timestamp: Date.now(),
            checks: results
        };
    }
}

14. Metrics Collection Pattern

Theory: Collects and exposes application metrics for monitoring.

Code:

class MetricsCollector {
    constructor() {
        this.counters = new Map();
        this.gauges = new Map();
        this.histograms = new Map();
    }

    incrementCounter(name, labels = {}, value = 1) {
        const key = this.createKey(name, labels);
        this.counters.set(key, (this.counters.get(key) || 0) + value);
    }

    setGauge(name, labels = {}, value) {
        const key = this.createKey(name, labels);
        this.gauges.set(key, { value, timestamp: Date.now() });
    }

    recordHistogram(name, labels = {}, value) {
        const key = this.createKey(name, labels);
        if (!this.histograms.has(key)) {
            this.histograms.set(key, { count: 0, sum: 0, buckets: new Map() });
        }
        
        const hist = this.histograms.get(key);
        hist.count++;
        hist.sum += value;
        
        const buckets = [1, 5, 10, 25, 50, 100, 250, 500, 1000];
        buckets.forEach(bucket => {
            if (value <= bucket) {
                hist.buckets.set(bucket, (hist.buckets.get(bucket) || 0) + 1);
            }
        });
    }

    createKey(name, labels) {
        const labelStr = Object.entries(labels)
            .map(([k, v]) => `${k}="${v}"`)
            .join(',');
        return labelStr ? `${name}{${labelStr}}` : name;
    }
}

15. Async Processing Pattern

Theory: Processes tasks asynchronously to improve responsiveness.

Code:

class AsyncProcessor {
    constructor(options = {}) {
        this.maxConcurrency = options.maxConcurrency || 5;
        this.queue = [];
        this.running = 0;
        this.tasks = new Map();
    }

    async process(operation, priority = 0) {
        const taskId = require('uuid').v4();
        
        return new Promise((resolve, reject) => {
            const task = {
                id: taskId,
                operation,
                priority,
                resolve,
                reject,
                status: 'queued',
                createdAt: Date.now()
            };
            
            this.tasks.set(taskId, task);
            this.queue.push(task);
            this.queue.sort((a, b) => b.priority - a.priority);
            
            this.processQueue();
        });
    }

    async processQueue() {
        if (this.running >= this.maxConcurrency || this.queue.length === 0) {
            return;
        }

        this.running++;
        const task = this.queue.shift();
        task.status = 'running';
        task.startedAt = Date.now();

        try {
            const result = await task.operation();
            task.status = 'completed';
            task.completedAt = Date.now();
            task.resolve(result);
        } catch (error) {
            task.status = 'failed';
            task.error = error.message;
            task.reject(error);
        } finally {
            this.running--;
            this.processQueue();
        }
    }
}

16. Reactive Streams Pattern

Theory: Handles asynchronous data streams with backpressure support.

Code:

class ReactiveStream {
    constructor(source) {
        this.source = source;
        this.operators = [];
    }

    static fromArray(array) {
        return new ReactiveStream(array);
    }

    map(fn) {
        this.operators.push({ type: 'map', fn });
        return this;
    }

    filter(fn) {
        this.operators.push({ type: 'filter', fn });
        return this;
    }

    take(count) {
        this.operators.push({ type: 'take', count });
        return this;
    }

    subscribe(observer) {
        let data = Array.isArray(this.source) ? [...this.source] : this.source;
        let taken = 0;

        for (const op of this.operators) {
            switch (op.type) {
                case 'map':
                    data = data.map(op.fn);
                    break;
                case 'filter':
                    data = data.filter(op.fn);
                    break;
                case 'take':
                    data = data.slice(0, op.count);
                    break;
            }
        }

        try {
            data.forEach(item => observer.next && observer.next(item));
            observer.complete && observer.complete();
        } catch (error) {
            observer.error && observer.error(error);
        }
    }
}

17. API Gateway Pattern

Theory: Single entry point for client requests with routing and cross-cutting concerns.

Code:

class APIGateway {
    constructor() {
        this.routes = new Map();
        this.middleware = [];
    }

    addRoute(route) {
        this.routes.set(route.pathPattern, route);
    }

    async routeRequest(request) {
        const route = this.findRoute(request.path);
        if (!route) {
            throw new Error('Route not found');
        }

        // Apply middleware
        for (const middleware of this.middleware) {
            request = await middleware(request);
        }

        // Route to target service
        const targetUrl = `${route.targetService}${route.targetPath}`;
        const response = await this.makeRequest(targetUrl, request);
        
        return response;
    }

    findRoute(path) {
        for (const [pattern, route] of this.routes) {
            if (this.matchPath(pattern, path)) {
                return route;
            }
        }
        return null;
    }
}

18. Distributed Lock Pattern

Theory: Coordinates access to shared resources across distributed systems.

Code:

class DistributedLock {
    constructor() {
        this.locks = new Map();
    }

    async acquireLock(key, ttl = 30000) {
        const lockId = require('uuid').v4();
        const expiresAt = Date.now() + ttl;

        if (this.locks.has(key)) {
            const existingLock = this.locks.get(key);
            if (existingLock.expiresAt > Date.now()) {
                return null; // Lock already held
            }
        }

        this.locks.set(key, { lockId, expiresAt });
        return lockId;
    }

    async releaseLock(key, lockId) {
        const lock = this.locks.get(key);
        if (lock && lock.lockId === lockId) {
            this.locks.delete(key);
            return true;
        }
        return false;
    }

    async withLock(key, operation, ttl = 30000) {
        const lockId = await this.acquireLock(key, ttl);
        if (!lockId) {
            throw new Error('Could not acquire lock');
        }

        try {
            return await operation();
        } finally {
            await this.releaseLock(key, lockId);
        }
    }
}

19. Idempotency Pattern

Theory: Ensures operations can be safely retried without side effects.

Code:

class IdempotencyService {
    constructor() {
        this.cache = new Map();
    }

    generateKey(...args) {
        return require('crypto')
            .createHash('sha256')
            .update(JSON.stringify(args))
            .digest('hex');
    }

    async executeIdempotent(key, operation) {
        if (this.cache.has(key)) {
            return this.cache.get(key);
        }

        const result = await operation();
        this.cache.set(key, result);
        
        // Set expiration
        setTimeout(() => this.cache.delete(key), 300000);
        
        return result;
    }
}

20. Materialized View Pattern

Theory: Pre-computed views of data for improved query performance.

Code:

class MaterializedViewService {
    constructor() {
        this.views = new Map();
        this.refreshInterval = 60000;
    }

    createView(name, queryFunction, refreshInterval) {
        const view = {
            name,
            queryFunction,
            refreshInterval: refreshInterval || this.refreshInterval,
            data: null,
            lastRefresh: null
        };
        this.views.set(name, view);
        this.refreshView(name);
        setInterval(() => this.refreshView(name), view.refreshInterval);
    }

    getView(name) {
        const view = this.views.get(name);
        return view ? view.data : null;
    }
}

21. Cache Warming Pattern

Theory: Proactively loads data into cache before it's requested.

Code:

class CacheWarmingService {
    constructor(cache) {
        this.cache = cache;
    }

    async warmCache(cacheSpecs) {
        const results = [];
        for (const spec of cacheSpecs) {
            try {
                const data = await spec.dataLoader();
                this.cache.set(spec.key, data, spec.ttl);
                results.push({ key: spec.key, success: true });
            } catch (error) {
                results.push({ key: spec.key, success: false, error: error.message });
            }
        }
        return results;
    }
}

22. Blue-Green Deployment Pattern

Theory: Zero-downtime deployment by switching between two environments.

Code:

class BlueGreenDeployment {
    constructor() {
        this.activeEnvironment = 'blue';
        this.switchInProgress = false;
    }

    async switchTraffic() {
        if (this.switchInProgress) {
            throw new Error('Switch already in progress');
        }
        
        this.switchInProgress = true;
        const previousEnv = this.activeEnvironment;
        this.activeEnvironment = this.activeEnvironment === 'blue' ? 'green' : 'blue';
        this.switchInProgress = false;
        
        return `Switched from ${previousEnv} to ${this.activeEnvironment}`;
    }

    getStatus() {
        return {
            activeEnvironment: this.activeEnvironment,
            switchInProgress: this.switchInProgress
        };
    }
}

23. Inbox Pattern

Theory: Ensures exactly-once message processing by tracking processed messages.

Code:

class InboxPattern {
    constructor() {
        this.processedMessages = new Map();
        this.processedCount = 0;
    }

    async handleMessage(messageId, eventData) {
        if (this.processedMessages.has(messageId)) {
            return false; // Already processed
        }
        
        this.processedMessages.set(messageId, eventData);
        this.processedCount++;
        return true;
    }

    getStats() {
        return {
            processedCount: this.processedCount,
            pendingCount: 0
        };
    }
}

24. Strangler Fig Pattern

Theory: Gradually replaces legacy systems by routing traffic to new implementations.

Code:

class StranglerFig {
    constructor() {
        this.migrationRules = new Map();
        this.migrationStats = new Map();
    }

    addMigrationRule(path, useNewService, percentage = 100) {
        this.migrationRules.set(path, { useNewService, percentage });
        this.migrationStats.set(path, { newService: 0, legacyService: 0 });
    }

    async routeRequest(path, requestData) {
        const rule = this.migrationRules.get(path);
        const stats = this.migrationStats.get(path);
        
        if (rule && rule.useNewService) {
            stats.newService++;
            return this.processWithNewService(requestData);
        } else {
            stats.legacyService++;
            return this.processWithLegacyService(requestData);
        }
    }

    processWithNewService(data) {
        return { result: 'processed by new service', version: 'v2' };
    }

    processWithLegacyService(data) {
        return { result: 'processed by legacy service', version: 'v1' };
    }
}

25. Timeout Pattern

Theory: Prevents operations from running indefinitely by setting time limits.

Code:

class TimeoutService {
    async executeWithTimeout(operation, timeoutMs = 5000) {
        return Promise.race([
            operation(),
            new Promise((_, reject) => 
                setTimeout(() => reject(new Error('Operation timed out')), timeoutMs)
            )
        ]);
    }
}

26. Write-Behind Cache Pattern

Theory: Writes to cache immediately and persists to storage asynchronously.

Code:

class WriteBehindCache {
    constructor() {
        this.cache = new Map();
        this.writeQueue = [];
        this.processing = false;
    }

    set(key, value) {
        this.cache.set(key, value);
        this.writeQueue.push({ key, value, timestamp: Date.now() });
        this.processWriteQueue();
    }

    async processWriteQueue() {
        if (this.processing) return;
        this.processing = true;
        
        while (this.writeQueue.length > 0) {
            const item = this.writeQueue.shift();
            try {
                await this.persistToStorage(item.key, item.value);
            } catch (error) {
                console.error('Write-behind failed:', error);
            }
        }
        
        this.processing = false;
    }
}

27. Anti-Corruption Layer Pattern

Theory: Isolates domain model from external systems with translation layer.

Code:

class AntiCorruptionLayer {
    translateFromExternal(externalData) {
        return {
            id: externalData.external_id,
            name: externalData.display_name,
            status: this.mapStatus(externalData.state)
        };
    }

    translateToExternal(domainObject) {
        return {
            external_id: domainObject.id,
            display_name: domainObject.name,
            state: this.mapToExternalStatus(domainObject.status)
        };
    }

    mapStatus(externalStatus) {
        const statusMap = {
            'ACTIVE': 'active',
            'INACTIVE': 'inactive',
            'PENDING': 'pending'
        };
        return statusMap[externalStatus] || 'unknown';
    }
}

28. Canary Deployment Pattern

Theory: Gradually rolls out changes to a subset of users.

Code:

class CanaryDeployment {
    constructor(canaryPercentage = 10) {
        this.canaryPercentage = canaryPercentage;
        this.metrics = {
            canaryRequests: 0,
            stableRequests: 0,
            canaryErrors: 0,
            stableErrors: 0
        };
    }

    shouldUseCanary(context = {}) {
        if (context.userId) {
            const hash = this.hashCode(context.userId) % 100;
            return hash < this.canaryPercentage;
        }
        return false;
    }

    processRequest(data, context = {}) {
        const useCanary = this.shouldUseCanary(context);
        
        try {
            if (useCanary) {
                this.metrics.canaryRequests++;
                return { result: data.value * 3, version: 'v2-canary' };
            } else {
                this.metrics.stableRequests++;
                return { result: data.value * 2, version: 'v1-stable' };
            }
        } catch (error) {
            if (useCanary) {
                this.metrics.canaryErrors++;
            } else {
                this.metrics.stableErrors++;
            }
            throw error;
        }
    }
}

29. Message Producer Pattern

Theory: Publishes messages to messaging systems for asynchronous communication.

Code:

class MessageProducer {
    constructor() {
        this.topics = new Map();
        this.subscribers = new Map();
    }

    async publishMessage(topic, message) {
        const subscribers = this.subscribers.get(topic) || [];
        const promises = subscribers.map(subscriber => 
            this.deliverMessage(subscriber, message)
        );
        
        await Promise.allSettled(promises);
    }

    subscribe(topic, callback) {
        if (!this.subscribers.has(topic)) {
            this.subscribers.set(topic, []);
        }
        this.subscribers.get(topic).push(callback);
    }

    async deliverMessage(subscriber, message) {
        try {
            await subscriber(message);
        } catch (error) {
            console.error('Message delivery failed:', error);
        }
    }
}

30. Event Stream Processor Pattern

Theory: Processes continuous streams of events in real-time.

Code:

class EventStreamProcessor {
    constructor() {
        this.eventHandlers = new Map();
        this.eventStats = {
            connected: true,
            eventCounts: {}
        };
    }

    onLocalEvent(eventType, handler) {
        if (!this.eventHandlers.has(eventType)) {
            this.eventHandlers.set(eventType, []);
        }
        this.eventHandlers.get(eventType).push(handler);
    }

    async publishEvent(topic, eventType, eventData, aggregateId) {
        const event = {
            id: require('uuid').v4(),
            type: eventType,
            data: eventData,
            aggregateId,
            timestamp: Date.now()
        };

        const handlers = this.eventHandlers.get(eventType) || [];
        handlers.forEach(handler => {
            try {
                handler(event);
                this.eventStats.eventCounts[eventType] = 
                    (this.eventStats.eventCounts[eventType] || 0) + 1;
            } catch (error) {
                console.error('Event handler failed:', error);
            }
        });
    }
}

31. Token Bucket Rate Limiter Pattern

Theory: Controls request rate using token bucket algorithm.

Code:

class TokenBucketRateLimiter {
    constructor(options = {}) {
        this.capacity = options.capacity || 100;
        this.refillRate = options.refillRate || 10;
        this.tokens = this.capacity;
        this.lastRefill = Date.now();
    }

    isAllowed(clientId) {
        this.refill();
        
        if (this.tokens >= 1) {
            this.tokens--;
            return { allowed: true };
        }
        
        return { 
            allowed: false, 
            resetTime: Math.ceil((1 / this.refillRate) * 1000) 
        };
    }

    refill() {
        const now = Date.now();
        const timePassed = (now - this.lastRefill) / 1000;
        const tokensToAdd = timePassed * this.refillRate;
        
        this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);
        this.lastRefill = now;
    }
}

32. Two-Phase Commit Pattern

Theory: Ensures atomicity across distributed transactions.

Code:

class TwoPhaseCommitCoordinator {
    constructor() {
        this.participants = new Map();
        this.transactions = new Map();
    }

    registerParticipant(id, participant) {
        this.participants.set(id, participant);
    }

    async executeTransaction(participantIds, transactionData) {
        const transactionId = require('uuid').v4();
        
        try {
            // Phase 1: Prepare
            const preparePromises = participantIds.map(id => {
                const participant = this.participants.get(id);
                return participant.prepare(transactionId, transactionData);
            });
            
            const prepareResults = await Promise.all(preparePromises);
            const allPrepared = prepareResults.every(result => result === true);
            
            if (allPrepared) {
                // Phase 2: Commit
                const commitPromises = participantIds.map(id => {
                    const participant = this.participants.get(id);
                    return participant.commit(transactionId);
                });
                
                await Promise.all(commitPromises);
                return { success: true, transactionId };
            } else {
                // Abort
                const abortPromises = participantIds.map(id => {
                    const participant = this.participants.get(id);
                    return participant.abort(transactionId);
                });
                
                await Promise.all(abortPromises);
                return { success: false, transactionId };
            }
        } catch (error) {
            return { success: false, error: error.message, transactionId };
        }
    }
}