Real-world combination
Event Bus = Observer + Mediator
A single hub (Mediator) lets modules subscribe and react to named events (Observer) without ever knowing about each other.
✗ The problem
Every module wired to every other module
App-wide events — login, cart change, theme switch — need to reach many unrelated modules. Wire them directly and you get a tangled many-to-many mess.
function login(user) {
navbar.greet(user);
cart.sync(user);
analytics.track('login', user);
chat.connect(user); // ← new consumer? edit here too…
}
Every module must know about every other module. Add one, and you edit every call site
that touches it — a combinatorial explosion of dependencies.
✓ The combination
↕
↓
One hub, subscribed by name
A central Event Bus is a Mediator — the one hub everything talks to — that uses Observer semantics: subscribe and broadcast by event name, not by direct reference.
class EventBus {
handlers = {};
on(event, fn) {
(this.handlers[event] ??= []).push(fn);
}
off(event, fn) {
this.handlers[event] =
(this.handlers[event] || []).filter(h => h !== fn);
}
emit(event, data) {
(this.handlers[event] || []).forEach(fn => fn(data));
}
}
// bus.on('login', fn); bus.emit('login', user);
Module A
Module B
EventBus
on / emit / off
Subscriber(s) of that event
✓ See it live
Emit an event — only subscribers react
Navbar listens for login, Cart listens for addToCart, Analytics listens
for both. Emit an event and watch the bus fan it out to only the subscribed modules.
🔌 EventBus
↓ fan-out to subscribers ↓
🧭
Navbar
on('login')
—
🛒
Cart
on('addToCart')
—
📈
Analytics
on('login', 'addToCart')
—
no events emitted yet
Tip: click a module to unsubscribe it, then emit again.
✓ Takeaway
Decoupled by event name
- Decoupling: modules only know event names, never each other's identity or API.
- Named channels:
'login','addToCart'— any module can subscribe or emit. - Easy add/remove: new listeners plug in with
on(); no existing code changes. - Caution: global events are harder to trace/debug, ordering isn't guaranteed, and forgetting
off()leaks memory. - Real uses: Node's
EventEmitter, browserCustomEvent/DOM events, Redux middleware, Vue/React app-wide event buses.
🎯 Combines: Mediator
(a single hub decouples modules) + Observer (subscribe/emit by
event) — often implemented as a Singleton app-wide.
Back to all topics →