← All posts
6 min read

Publish-Subscribe Systems: Sharing Events with Many Consumers

#technology#distributed-systems#publish-subscribe#events
📑 On this page

One business event can matter to many systems.

When an order is placed, inventory reserves items, analytics records a sale, loyalty awards points, notification sends confirmation, and fraud detection updates its models.

Publish-subscribe distributes one event to multiple interested consumers without requiring the publisher to call each one directly.

This loosens direct dependencies, but it makes event meaning, delivery, replay, and consumer independence essential.

A concrete example: OrderPlaced

The order service publishes:

OrderPlaced {
  eventId: "evt-8472",
  orderId: 8472,
  customerId: 219,
  occurredAt: "2026-07-17T10:15:00Z"
}

Independent subscriptions deliver the event to:

  • inventory,
  • analytics,
  • email,
  • loyalty,
  • and fraud systems.

The order service does not need five direct network calls in its checkout request.

Publishers, topics, and subscribers

A publisher sends an event to a named topic or stream.

A subscription represents a consumer's interest and delivery state. Several subscribers can each receive the same event independently.

If one subscriber is down, others can continue, depending on broker configuration.

Events describe facts

An event usually states something that already happened:

  • OrderPlaced
  • PasswordChanged
  • ShipmentDelivered

Use past-tense names to emphasize that subscribers do not approve the event after the fact.

A command such as SendEmail asks one capability to do work and has different ownership semantics.

Pub-sub versus a work queue

In a work queue, several consumers often compete for messages so one worker handles each job.

In pub-sub, each subscription receives its own copy of the event. Ten email workers may compete within the email subscription, while analytics has a separate subscription and also receives the event.

The same broker can support both patterns through topic and subscription configuration.

Loose coupling

Publishers do not need to know:

  • how many subscribers exist,
  • where they run,
  • how quickly they process,
  • or which internal technology they use.

This makes adding a new consumer easier. The coupling moves to the event contract and its meaning.

Event contract

An event schema should define:

  • event identity,
  • event type,
  • occurrence time,
  • entity identifiers,
  • relevant business data,
  • version,
  • and semantic meaning.

Field names alone are insufficient. Consumers need to know whether an amount includes tax, whether cancellation can follow, and which system is authoritative.

Event notification versus event-carried state

A minimal event may say:

OrderChanged { orderId: 8472 }

Consumers then call the order service for details. This keeps messages small but restores runtime coupling and can retrieve a newer state than the event represented.

An event-carried state transfer includes enough data for consumers to act independently, at the cost of larger contracts and duplicated data.

Delivery guarantees

Pub-sub commonly provides at-least-once delivery.

Subscribers must handle:

  • duplicates,
  • delay,
  • reordering,
  • and replay.

The publisher should not assume that broker acceptance means every subscriber completed its reaction.

Consumer offsets

Log-based systems store events in ordered partitions.

Each consumer group tracks an offset indicating progress. A consumer can resume after restart or intentionally reset the offset to replay old events.

Offset commitment and business-state commitment need coordination to avoid loss or duplication.

Consumer groups

Multiple instances in one consumer group share a subscription's workload.

For example, five analytics workers divide partitions while the email group independently processes the same source events.

Adding workers improves parallelism only up to the available partition count and downstream capacity.

Ordering

Global event order is difficult and often unnecessary.

A topic may guarantee order only within a partition. Choosing orderId as a key keeps events for one order together while allowing different orders in parallel.

Consumers should still use versions or sequence numbers when stale events could overwrite newer state.

Replay

Retained event logs allow a consumer to rebuild derived state.

Examples:

  • recreate a search index,
  • backfill a new analytics model,
  • recover a corrupted view,
  • or test a new subscriber.

Replay means old events meet new code, so contracts, side effects, and idempotency need careful handling.

Side effects during replay

Replaying OrderPlaced should not resend years of customer emails.

Consumers should distinguish:

  • rebuilding internal state,
  • live operational reactions,
  • and irreversible external side effects.

Separate topics, replay modes, or idempotency records can protect against accidental repetition.

Event evolution

Producers and consumers deploy independently.

Compatible evolution generally:

  • adds optional fields,
  • preserves existing meaning,
  • avoids reusing fields for new semantics,
  • and supports old events during retention.

Breaking changes may require a new event version or parallel publication during migration.

The outbox pattern

Publishing must remain consistent with the producer's database update.

If an order commits but OrderPlaced is lost, subscribers never react. An outbox record written in the same local transaction allows a publisher process to deliver the event later.

Consumers still need deduplication because the outbox publisher may retry.

Slow subscribers

One subscriber can fall behind without blocking others.

Monitor:

  • backlog or consumer lag,
  • oldest unprocessed event,
  • processing rate,
  • failures,
  • and partition skew.

Retention must exceed realistic recovery time, or a slow subscriber may lose access to events it has not processed.

Subscriber failure isolation

A faulty analytics consumer should not prevent checkout or email.

Separate subscriptions, retry policies, resource quotas, and dead-letter handling isolate failures. Shared downstream databases can still create hidden coupling.

Logical decoupling does not guarantee resource isolation.

Event ownership

The domain that owns the fact should own its event meaning.

Consumers should not force the publisher to expose every internal field. A governance process can review contracts, privacy, and compatibility without turning one central team into a delivery bottleneck.

Ownership makes semantic change accountable.

Privacy and data minimization

Every subscriber receiving an event expands the data boundary.

Publish only fields needed for legitimate reactions. Sensitive data may need:

  • restricted topics,
  • encryption,
  • retention limits,
  • access auditing,
  • or reference by identifier instead of duplication.

An event bus should not become an uncontrolled copy of every customer record.

Observability

Connect:

  • publisher event identifier,
  • broker position,
  • subscription delivery,
  • consumer processing,
  • retries,
  • and final side effect.

Distributed tracing may not remain one continuous request, so stable event and causation identifiers are valuable.

Knowledge check

  1. How does pub-sub differ from competing consumers on one work queue?
  2. Why are events commonly named in past tense?
  3. What tradeoff exists between notification-only and event-carried-state messages?
  4. Why can replay accidentally repeat harmful side effects?
  5. How does the outbox pattern help event publication?

The one idea to remember

Publish-subscribe distributes one fact to independent consumer subscriptions and removes direct knowledge of each reaction from the publisher. That freedom depends on stable event meaning, duplicate-safe consumers, replay discipline, failure isolation, and careful data governance.