Mediator Design Pattern
I had a form where changing a dropdown updated a text field, which triggered a validation, which enabled a submit button, which showed a confirmation pane...
23 Mar 2024

I had a form where changing a dropdown updated a text field, which triggered a validation, which enabled a submit button, which showed a confirmation panel. Every component talked to every other component. Adding a new field meant touching five files. It was a spiderweb.
The Mediator pattern replaces that spiderweb with a hub. Components don't talk to each other directly. They talk to the mediator. The mediator decides who else needs to know.
Think of it like air traffic control. Planes don't negotiate with each other about landing order. They all talk to the tower. The tower coordinates.
class ChatRoom {
constructor() {
this.users = [];
}
addUser(user) {
this.users.push(user);
}
sendMessage(sender, message) {
console.log(`[${sender.getName()}]: ${message}`);
this.users
.filter(user => user !== sender)
.forEach(user => user.receive(message, sender));
}
}
class User {
constructor(name, mediator) {
this.name = name;
this.mediator = mediator;
mediator.addUser(this);
}
getName() {
return this.name;
}
sendMessage(message) {
this.mediator.sendMessage(this, message);
}
receive(message, from) {
console.log(`${this.name} received from ${from.getName()}: ${message}`);
}
}
const chatRoom = new ChatRoom();
const alice = new User("Alice", chatRoom);
const bob = new User("Bob", chatRoom);
alice.sendMessage("Hey Bob!");
// [Alice]: Hey Bob!
// Bob received from Alice: Hey Bob!
bob.sendMessage("Hi Alice!");
// [Bob]: Hi Alice!
// Alice received from Bob: Hi Alice!
ChatRoom is the mediator. User objects are colleagues. Alice doesn't know about Bob directly — she sends a message to the chat room, and the chat room routes it.
If you add a third user, Carol, only the ChatRoom logic might change (or nothing changes at all). Alice and Bob are unaffected.
Where I Use This
- UI forms where field changes trigger updates in other fields.
- Event buses in frontend frameworks.
- Chat systems where users communicate through a central server.
- Workflow engines where steps need coordination.
The benefit: Components are decoupled. Adding or removing a colleague doesn't require changing the others. Communication logic is centralized and easy to reason about.
The cost: The mediator itself can become a god object. All the coordination logic piles up in one place. If the mediator gets too complex, you've just moved the spiderweb from many files into one file. Also, debugging requires understanding the mediator's routing logic — the communication path isn't visible from the colleague's code.
I reach for the Mediator when I see many-to-many communication between objects. If it's just one-to-one or one-to-many, the Observer pattern is simpler.