Module 05 · Communication patterns · Lesson 7 of 10
Publish/subscribe
One event, many independent consumers. How publish/subscribe differs from a work queue, why it lets you add consumers without touching producers, how topics and subscriptions compose, and the coupling that remains in the schema.
Why it matters
When an order is placed, five teams care: inventory, shipping, email, analytics, fraud. If checkout calls each of them, checkout knows about all five, breaks when any is down, and must change every time a sixth appears. Publish/subscribe inverts the dependency: checkout announces "order placed" once, and whoever cares listens. It is the pattern that lets a system grow new behaviour without rewiring the old.
The idea
In publish/subscribe, producers publish messages to a named topic without knowing who receives them. Each subscriber registers interest in a topic and receives a copy of every message published to it. The broker does the fan-out. Contrast with a work queue, where each message is processed by exactly one of several competing workers.
Topics and subscriptions
A topic is the channel; a subscription is one consumer's durable attachment to it, with its own cursor or backlog. Each subscription receives every message; inside a subscription, a consumer group of several worker instances shares the messages like a work queue. So the shapes compose: fan-out across subscriptions, load-balancing within each. Topic naming is a design decision: too coarse (events) and every subscriber filters most of what it receives; too fine (order.placed.us.gold) and the topology becomes unmanageable. A common middle is one topic per entity or domain with an event type field, plus broker-side filtering where the broker supports it.
Events versus commands
A command tells one recipient to do something ("send this email") and belongs on a queue. An event states that something happened ("order placed") and belongs on a topic. The difference is who decides what happens next: with a command the producer decides; with an event each subscriber decides for itself. Events keep producers ignorant of consumers, which is the whole point.
The coupling that remains
Pub/sub removes the dependency on who consumes, but not on what the message means. Every subscriber depends on the event schema, so changing it is a breaking change for people you may not know about. The rules from the data-formats lesson apply with full force: additive changes only, a schema registry, versioned event types. Also decide what an event carries: a thin event with ids ("order 9001 placed") makes consumers call back for details; a fat event with the full snapshot avoids the call and risks staleness and size. Most systems settle on fat events for the fields consumers overwhelmingly need and ids for the rest.
Delivery and ordering
Fan-out is at-least-once per subscription, and a slow subscriber builds its own backlog without affecting others. Ordering holds per partition or per key within a subscription, not globally; if consumers need "updated after created", publish with the entity id as the key so all its events land in order on one partition. The Delivery semantics lesson covers the guarantees in detail.
| Work queue | Pub/sub topic | |
|---|---|---|
| Message goes to | One consumer | Every subscription |
| Producer knows | Which queue, and implicitly what work | Only the topic and schema |
| Adding a consumer | Changes producer behaviour (now two things do the work) | No producer change; new subscription |
| Message type | Commands | Events |
| Backlog | Shared | Per subscription |
| Typical use | Background jobs, task distribution | Domain events, integration between services, audit and analytics feeds |
Trade-offs
Pub/sub makes adding behaviour cheap and keeps producers simple, and in exchange moves complexity into the event schema, makes the flow of a request harder to follow (who reacts to this?), and makes debugging a matter of tracing across subscribers. It needs discipline around schemas and observability (correlation ids on every event) to stay understandable.
In an interview
Use the five-teams example in your own system: "Checkout publishes order.placed to a topic with the order id as key. Inventory, shipping, email and analytics each have a subscription with their own consumer group and backlog, so a slow analytics pipeline never delays shipping. The event schema is registered and additive-only; events carry the fields consumers need and ids for the rest." Then the limitation: "Ordering is per order id, and every subscriber is idempotent because delivery is at-least-once."
Check yourself
A fraud team wants to analyse every order without checkout being changed or slowed down. Which shape fits?