design·lab

Architecture

Publish / Subscribe

Publishers send messages to a topic on a broker; subscribers listen to topics they care about — neither side ever knows the other exists.

✗ The problem

One producer, calling every consumer directly

Placing an order must notify email, analytics, and the search index. Calling each one by hand couples the order service to all of them — and blocks the request until every call returns.

class OrderService {
  placeOrder(order) {
    save(order);
    email.send(order);       // ← coupled
    analytics.track(order);  // ← coupled
    search.index(order);      // ← coupled, blocks
  }
}
Add a new consumer → edit OrderService again. One slow consumer slows down every order.
✓ How it works

Invert it: publish to a topic, let the broker fan out

Publishers send messages to a named topic. Subscribers register on that topic. The broker delivers to everyone subscribed — publisher and subscribers never reference each other.

broker.subscribe(
  'order.created', email.send);
broker.subscribe(
  'order.created', analytics.track);

broker.publish(
  'order.created', order);
// → broker fans out to every
//   subscriber of that topic
Publisher
Broker
topic: order.created
Subscriber A
Subscriber B
Compare with Observer: same broadcast idea, but Observer runs in one process with direct object references. Pub/Sub puts a broker in between — publishers and subscribers can live in different processes, even different machines, and only agree on a topic name.
✓ See it live

Publish to a topic — watch the broker fan it out

Publish order.created or payment.failed. Only subscribers on the matching topic light up. Click a subscriber to unsubscribe it, then publish again.

📡 Broker  waiting…
✉️
Email
order.created
📊
Analytics
order.created
🔎
Search Index
order.created
🚨
Fraud Alert
payment.failed
0 subscribers notified
✓ Takeaway

Decoupling through a broker

  • Decoupling at scale: publishers and subscribers only agree on a topic name, not on each other — they can live in different services, languages, or machines.
  • Fan-out: one publish reaches every subscriber of a topic, automatically.
  • Add / remove consumers freely: the publisher's code never changes.
  • Async: the publisher doesn't wait on subscribers to finish their work.
  • Careful: delivery guarantees (at-least-once vs exactly-once), message ordering, and duplicate handling are real design decisions — the broker doesn't solve them for free.
  • Seen in: Kafka, Redis Pub/Sub, AWS SNS, MQTT.
🎯 Combines / relates: Pub/Sub is Observer scaled across processes via a broker; underpins event-driven architecture, queues, and event sourcing.