Stackbook Logo
messagingestablished · medium operational burden

Publish-Subscribe

Also known as: pub-sub, topic-based messaging

Intent

Decouple producers from consumers by broadcasting messages to a topic that any number of independent subscribers receive.

Problem

A producer that calls each interested service directly must know every consumer, and adding one means changing and redeploying the producer. Slow or failed consumers block the producer. Teams cannot evolve independently.

Forces

  • •Producers should not know who consumes their events
  • •Subscribers have different speeds and availability
  • •New consumers appear over time without producer changes
  • •Delivery guarantees and retention must be explicit

Solution

Overview

Publish-subscribe lets a producer emit a fact once, such as OrderPlaced, and any number of subscribers react to it. The producer talks only to the broker, never to consumers, so the set of listeners can change at will.

Each subscription is an independent cursor over the topic. Billing, shipping and analytics each get every message, at their own pace, without affecting one another.

In practice teams choose between broker styles. Log-based brokers retain messages so new subscribers can replay history, while queue-based brokers delete after acknowledgement. Decide early which model you need, because replay changes how you handle bugs, backfills and new consumers.

How it works

The broker fans a published message out to every subscription. Each subscription tracks its own acknowledgements, so one slow consumer does not delay others.

producer --publish--> [ topic: orders ]
                          |-- sub: billing   (offset 120)
                          |-- sub: shipping  (offset 118)
                          \-- sub: analytics (offset 97)

Delivery is usually at least once, so a subscriber may see the same message again after a timeout. Subscription filters, such as matching on event type or tenant attribute, reduce wasted work. Operationally, monitor lag per subscription, since each one fails and recovers independently, and set alerts on the oldest unacknowledged message rather than only on queue size.

Pitfalls

  • Treating events as commands: a subscriber that must act is really being called, so name and design the message as a fact.
  • Skipping idempotency. Brokers redeliver on timeouts, so handlers must tolerate duplicates.
  • Unbounded retention differences. A subscriber offline longer than retention silently loses data.
  • Changing message shape without versioning, which breaks unknown subscribers.

Key takeaways

  • Publish facts, not instructions.
  • Each subscription has independent progress and failure.
  • Design every handler to be idempotent.
  • Version and own your event contracts.

✓ When to Use

  • •One event interests many independent consumers
  • •Consumers are added or removed without coordinating with the producer
  • •Producers must not be slowed by consumer speed

✗ When Not to Use

  • •The caller needs an immediate response from a single callee
  • •Strict global ordering across all subscribers is required
  • •Only one consumer will ever exist and a direct queue is simpler

Pros

  • +Loose coupling between teams and services
  • +Easy to add new consumers
  • +Producer latency independent of consumers

Cons

  • −Harder to trace who reacts to what
  • −At-least-once delivery forces idempotent consumers
  • −Broker becomes critical shared infrastructure

Cost Profile

Infrastructure

Medium - managed broker or cluster

Operational

Medium - topic, ACL and retention management

Cognitive

Medium - implicit flows

Failure Modes

  • Slow subscriber backlog grows until retention drops messages

  • Duplicate delivery causes double side effects

  • Topic sprawl and unclear ownership of event contracts

Real-World Examples

Alternatives

    Related Patterns

    • event-driven-architecture
    • dead-letter-queue
    • outbox-pattern

    Competency Domains

    distribution communicationdata state