Event Bus et le pattern Observer en JavaScript (Node.js)
Dans le développement moderne avec Node.js, il est souvent nécessaire d'organiser l'interaction entre différents composants d'une application sans créer de liens rigides entre eux. Le pattern Observer (Observateur) et son implémentation plus puissante — l'Event Bus (Bus d'événements) — offrent une solution élégante à ce problème. Dans cet article, nous allons détailler comment implémenter ces patterns en JavaScript pur dans l'environnement Node.js, examiner leurs avantages et leurs cas d'utilisation typiques.
Qu'est-ce que le pattern Observer ?
Le pattern Observer définit une relation "un à plusieurs" entre les objets : lorsqu'un objet (le sujet) change d'état, tous les objets qui en dépendent (les observateurs) en sont automatiquement notifiés. Dans le contexte de Node.js, ce pattern est à la base du module intégré events, qui fournit la classe EventEmitter.
Event Bus : extension du concept
L'Event Bus est un bus d'événements centralisé qui permet à tout composant d'une application d'envoyer et de recevoir des messages sans se connaître directement. Cela est particulièrement utile dans une architecture de microservices, des applications complexes avec une structure modulaire ou lors de la création de systèmes de plugins.
Implémentation d'un Event Bus en Node.js
Voyons comment créer notre propre Event Bus à partir de zéro. Notre classe prendra en charge les méthodes principales : abonnement aux événements (on), abonnement unique (once), suppression des abonnés (off) et émission d'événements (emit).
class EventBus { constructor() { this.events = {}; }
on(event, listener) { if (!this.events[event]) { this.events[event] = []; } this.events[event].push(listener); return () => this.off(event, listener); }
once(event, listener) { const wrapper = (...args) => { listener(...args); this.off(event, wrapper); }; this.on(event, wrapper); return () => this.off(event, wrapper); }
off(event, listener) { if (!this.events[event]) return; this.events[event] = this.events[event].filter(l => l !== listener); }
emit(event, ...args) { if (!this.events[event]) return false; this.events[event].forEach(listener => listener(...args)); return true; }}
// Exemple d'utilisationconst bus = new EventBus();
const unsubscribe = bus.on('user:login', (username) => { console.log(`Utilisateur ${username} connecté`);});
bus.once('app:start', () => { console.log('Application démarrée (notification unique)');});
// Émission d'événementsbus.emit('app:start'); // "Application démarrée (notification unique)"bus.emit('user:login', 'Anna'); // "Utilisateur Anna connecté"
// Désabonnementunsubscribe();bus.emit('user:login', 'Ivan'); // Rien ne se produitUtilisation de l'EventEmitter intégré de Node.js
Node.js fournit une implémentation prête à l'emploi d'EventEmitter dans le module events. Cependant, un Event Bus créé à partir de celui-ci permet de gérer centralement tous les événements de l'application.
const EventEmitter = require('events');
class AppEventBus extends EventEmitter { constructor() { super(); this.setMaxListeners(50); // Augmente la limite d'abonnés }
// Ajout de journalisation pour le débogage emit(event, ...args) { console.log(`[EventBus] Événement : ${event}`); return super.emit(event, ...args); }}
const appBus = new AppEventBus();
// Abonnement aux événementsappBus.on('order:created', (order) => { console.log(`Commande #${order.id} créée. Envoi d'une notification.`);});
appBus.on('order:created', (order) => { console.log(`Commande #${order.id} créée. Mise à jour du stock.`);});
// ÉmissionappBus.emit('o Aussi dans la bibliothèque
Configuration d'applications en C++ : bibliothèques et outils pour un paramétrage flexible
Planificateur de tâches en Node.js : node-cron, node-schedule et Bull
Planificateur de tâches en C# (.NET) : Quartz.NET, Hangfire, Coravel
Retrofit Java : guide complet avec exemples concrets