Publish-Subscribe vs Point-to-Point Queues
What This Concept Is
Two fundamentally different delivery models sit under every messaging system:
Point-to-point (queue)
One message, one consumer. N workers pull from the same queue; whichever worker grabs the message first gets it; the others do not see it. This is work distribution -- a shared to-do list.
+---- [worker A] (got msg 1)
|
producer --> | msg 1 | --+---- [worker B] (got msg 2)
| msg 2 | |
| msg 3 | +---- [worker C] (got msg 3)
Canonical form: AMQP queue, SQS standard queue, JMS queue. One consumer per message; horizontal scale is adding workers.
Publish-subscribe (topic)
One message, every subscriber gets its own copy. This is broadcast -- tell anyone who cares.
+---> [subscriber: billing] (copy of msg 1)
|
producer --> (topic: orders) --+---> [subscriber: inventory] (copy of msg 1)
|
+---> [subscriber: analytics] (copy of msg 1)
Canonical form: JMS topic, AMQP fanout exchange, SNS topic, Kafka topic with separate consumer groups (see Concept 09). Each subscriber gets every message independently; horizontal scale is adding subscribers without touching producers.
Why It Matters Here
Choosing between these two is the most consequential decision you make per message. Picking wrong produces specific failure modes:
- Pub-sub where you meant work distribution -> every subscriber does the same thing. You charge the card N times, you send N emails. Consumers end up inventing "idempotency keys" to fake queue semantics.
- Queue where you meant broadcast -> only one of the services that needs to react ever sees the message. Debugging the "missing" reactions takes weeks because nothing is obviously broken: the queue says "delivered."
The intent (Cluster 1) maps cleanly to the model:
- commands -> point-to-point queues (exactly one handler)
- events -> publish-subscribe topics (zero to many subscribers)
Concrete Example
Use point-to-point for commands
API ---> queue "payments.charge" ---> [worker 1 of 10]
[worker 2 of 10]
...
ChargeCard(order_id, amount, card_token) is a command. Exactly one worker should charge exactly once. Scale horizontally by adding workers; they share the queue.
Use pub-sub for events
payment service --> topic "payment.captured"
|
+---> [ledger] (records the transaction)
+---> [notify] (emails the receipt)
+---> [analytics] (updates dashboards)
+---> [loyalty] (awards points)
PaymentCaptured(order_id, amount) is a fact. Four services each get a full copy. Adding fraud-analytics next month is a new subscription; nothing about the payment service changes.
The hybrid that bites you
Many brokers let you do "queue groups inside a topic" (Kafka consumer groups, NATS queue groups). That is both models at once:
- across consumer groups -> pub-sub (each group gets every message)
- inside a consumer group -> point-to-point (one consumer per message)
This is powerful but often misused. A team runs one "consumer group" per service, which gives them pub-sub across services and work distribution within each service. This is the right default -- but only if you remember you are combining the two models.
Common Confusion / Misconception
"SQS is pub-sub." No. SQS is a queue; one consumer gets each message. To broadcast, you put an SNS topic in front and fan out to multiple SQS queues. The AWS convention is "SNS for pub-sub, SQS for point-to-point."
"Kafka is pub-sub." Kafka is a log with both models available through consumer groups. One consumer group = work distribution (partitioned). N consumer groups = pub-sub (each group reads independently).
"If we have 10 subscribers, pub-sub is better." Not if each subscriber is just a worker of the same logical task. Ten workers processing the same message ten times is a bug, not an architecture.
"Queues are old-fashioned and log-based systems replace them." They solve different problems. Queues excel at task distribution with at-least-once delivery and DLQs. Logs excel at replayable event streams and multiple independent consumers at different speeds. See Concepts 07 and 08.
How To Use It
Picking the model, per message:
Naming conventions that make the model obvious to the next engineer:
| Convention | Example | Intent |
|---|---|---|
*.commands / cmd.* | payment.commands.charge | Point-to-point |
| Past-tense topic | order.placed, payment.captured | Pub-sub |
*.dlq | payment.commands.charge.dlq | Dead-letter for either model |
Check Yourself
- Name one message type that must be point-to-point and one that must be pub-sub. Defend each in a sentence.
- A team has ten "consumers" of
user-signed-up-- they turn out to all be doing the same "send welcome email" with slight variations. Queue or topic? Why? - What happens to an SNS message that has no subscribers when it is published?
Mini Drill or Application
For a checkout workflow (order placed -> charge -> reserve inventory -> ship -> notify), in 15 minutes:
- List the 6-8 messages you would send.
- Classify each as point-to-point or pub-sub.
- Name the topic or queue explicitly with a naming convention you chose.
- Mark the DLQ strategy for each.
Transfer to Adjacent Domains
- Cluster 3 (brokers). Pub-sub vs point-to-point is a semantic choice; queues vs log-based brokers is a substrate choice. Kafka offers both (across consumer groups = pub-sub, within a group = point-to-point). Clarifying intent in this concept tells you what to configure in the next cluster.
- Cluster 4 (saga choreography). A choreographed saga is built from pub-sub events and point-to-point command queues braided together. Mis-modelling a "command as a broadcast event" is one of the two leading saga-failure patterns.
- Scalability (S8M1). The two models imply different scale-out levers: pub-sub scales by adding subscribers (each with its own pipeline); point-to-point scales by adding workers behind one queue. Autoscaling rules differ accordingly -- do not wire one set of CloudWatch alarms for both.
- Webhooks / SaaS integrations. External webhooks are fundamentally pub-sub over HTTP. The same "many independent subscribers" semantics apply; the substrate is the HTTP delivery+retry loop rather than a broker.
Read This Only If Stuck
- Richards & Ford: Asynchronous Capabilities -- the asynchronous posture that both models support
- Richards & Ford: Event-Driven Architecture Style -- broker topology with multiple event processors as pub-sub in practice
- Richards & Ford: Mediator Topology -- the command-routing end of the same spectrum
- System Design Primer: Asynchronism -- compact queue vs task-queue framing
- System Design Primer: Availability patterns -- why independent subscribers improve availability envelopes
- Enterprise Integration Patterns: Publish-Subscribe Channel and Point-to-Point Channel -- the canonical definitions
- AWS: Fanout notifications from SNS to SQS -- the production recipe for "pub-sub in front, point-to-point behind"
- AWS: Event-driven architecture overview -- vendor framing that names the two models in EventBridge terms
- Confluent: Apache Kafka 101 -- Consumer Groups -- how log-based brokers combine the two models through consumer groups
- Martin Fowler: Event-Driven -- four lenses -- notification and ECST both live on pub-sub topics; pick the delivery model first, then the content lens