We use analytics cookies.We use cookies to analyze site traffic and improve your experience.
Loading...
System Design Masterclass · Microservices Architecture
Free preview: the first 2 parts of this 8-part lesson.
Already bought the course? Sign in to read the whole lesson.
this lesson is part of
System Design Masterclass
Unlock this lesson and the 751 other paid lessons
$25 once, by PayPal
See the System Design courseread the rest of this lesson in
System Design Masterclass
Unlock this lesson and the 751 other paid lessons
$25 once, by PayPal
See the System Design coursein Microservices Architecture
This lesson is part of the full System Design Masterclass. Unlock it along with 769 other lessons for a one-time payment.
One-time payment by card or PayPal
Get Lifetime AccessOne-time payment: UPI, card or netbanking
Get Lifetime Access18 Foundation lessons are free, no payment needed. Try them now
It is easy and good
“It is really nice and good Please join the course”
You're building an e-commerce platform. A customer places an order. Now you need to:
The naive approach? Stuff all five actions into the placeOrder() function. It works. Until the marketing team wants to trigger an abandoned cart campaign. And the finance team wants real-time revenue tracking. And the fraud team wants to analyze purchase patterns.
Suddenly your placeOrder() function is 200 lines long, knows about email, inventory, warehouses, analytics, loyalty, marketing, finance, and fraud. It's a god function that does everything and changes every week because someone needs to add yet another reaction to "an order was placed."
The Observer pattern flips this. Instead of placeOrder() naming every downstream system, the order object keeps a list of observers, and each interested part of the code (email, inventory, analytics, loyalty) adds itself to that list. When an order is placed, placeOrder() walks the list and calls the same method on each one. It still holds a reference to every observer, but it no longer knows what any of them is or what it does with the news.
The Observer pattern defines a one-to-many relationship between objects. When one object (the subject or publisher) changes state, all dependent objects (the observers or subscribers) are notified automatically.
Think of a teacher with a class register. Pupils who want the homework write their name on the register. When the homework is set, the teacher goes down the register and tells each name on it. The teacher does not need to know why each pupil wants it, and a new pupil joining is one more line on the register, not a change to how the teacher works. But the teacher does hold the register: the list of listeners lives with the thing being watched.
| Concept | Role | Example |
|---|---|---|
| Subject | Holds state and the list of observers, calls each one on a change | Order service, stock price feed |
| Observer | Registers with the subject, reacts when called | Email sender, inventory updater, analytics |
| Notification | The call, often carrying what changed | onOrderPlaced(order), update(price) |
The critical property: the subject knows its observers only through one shared interface. It holds a direct reference to each observer in its list and calls them itself, usually one after another on the same thread. It does not know their concrete classes. The observers, for their part, know the subject, because they called subscribe() on it. There is no broker or event bus in the middle. That loose but direct link is what lets you add a new observer without editing the subject's code.
Here is the whole pattern in a few lines of JavaScript. The subject is a plain array of observers plus a loop.
class OrderService {
constructor() { this.observers = []; } // the subject keeps real references
subscribe(observer) { this.observers.push(observer); }
unsubscribe(observer) { this.observers = this.observers.filter((o) => o !== observer); }
placeOrder(order) {
console.log(`order ${order.id} placed`);
for (const o of this.observers) o.onOrderPlaced(order); // direct method calls, in order
}
}
const email = { onOrderPlaced: (o) => console.log(` email: confirmation for ${o.id}`) };
const inventory = { onOrderPlaced: (o) => console.log(` inventory: reserve ${o.qty} x ${o.sku}`) };
const orders = new OrderService();
orders.subscribe(email);
orders.subscribe(inventory);
orders.placeOrder({ id: "A-101", sku: "mug", qty: 2 });
orders.unsubscribe(email);
orders.placeOrder({ id: "A-102", sku: "pen", qty: 5 });
Running it with Node prints:
order A-101 placed
email: confirmation for A-101
inventory: reserve 2 x mug
order A-102 placed
inventory: reserve 5 x pen
After unsubscribe(email) the second order notifies only inventory. Nothing in placeOrder() changed.
Observer is not pub/sub. In publish/subscribe, publishers send messages to a named topic on a broker (, SNS, Google Pub/Sub), and subscribers read from that topic. Neither side holds a reference to the other, and the broker can store messages until a subscriber is ready. In Observer the subject holds the references and makes the calls itself, inside one program, and nothing is stored if an observer is not there. Pub/sub carries the same idea across a network, with a broker taking the subject's job of keeping the list. The comparison slide below goes through the differences one by one.