Skip to content

Messaging Patterns ​

Messaging decouples services in time and deployment. A queue is better suited to handing a job to one consumer; an event stream stores events so multiple consumers can process them at their own pace.

Quick Decision ​

NeedPatternWatch out
Hand work to workersQueue + competing consumersAcknowledgment and duplicates
Notify many consumersPub/Sub or event streamSchema ownership and replay
Wait for a message resultRequest-replyTimeout and correlation ID
Long-lived data flowPartitioned event streamOrdering key and consumer lag

Production Checklist ​

  • Problem: Does messaging solve coupling, burst absorption, or replay?
  • Solution: Are delivery, acknowledgment, ordering, retry, DLQ, schema, and ownership clear?
  • Trade-off: Async systems decouple services but add eventual consistency, duplicates, and operational cost.
  • Failure mode: Handle poison messages, consumer lag, broker outages, out-of-order events, and retry storms.
  • Measurement: Track queue depth, oldest message age, lag, processing latency, retries, and DLQ rate.
  • Security/cost: Manage topic/queue access, encryption, retention, payload PII, and broker storage cost.

Queue and Event Stream ​

ModelTypical technologySemantics
Broker queueRabbitMQ, AWS SQS, Azure Service Bus, Google Pub/SubA message usually leaves after successful acknowledgment
Event streamKafka, AWS Kinesis, Azure Event Hubs, NATS JetStreamReplay is possible through retention and offsets

Choose using ordering, retention, consumer count, managed operations, replay, cross-region needs, and cost—not throughput alone.

Message Patterns ​

Request-reply ​

The request carries a correlationId and reply destination. Bound the wait with a deadline; a late reply must not accidentally apply a business write twice.

Publish-subscribe ​

The producer publishes to a topic and each consumer reads through its subscription. Topic-based routing is simple and observable; content-based filtering can reduce consumers but adds broker filtering cost and schema coupling. Fan-out needs consumer isolation and a retention plan.

Competing consumers ​

Workers sharing one queue divide the work. If ordering matters, route one aggregate's messages to the same partition/consumer. Worker concurrency must stay below downstream connection-pool and database capacity.

Event Routing and Event-Driven Services ​

Route with a topic exchange, partition key, subject, or content filter. An unstable routing key creates hot partitions or ordering violations. The producer should publish a domain fact; each consumer owns its projection or side effect.

Choreography can be simple when one event triggers several services. For critical workflows, orchestration makes observability and compensation more explicit. Catalog the producer, consumers, schema owner, and deprecation policy for every event.

Schema Evolution ​

Preserve backward/forward compatibility with additive fields, unknown-field tolerance, and defaults. Field deletion, type changes, or semantic changes require a new event version/type. Consumers should safely quarantine unknown versions.

If a schema registry or central catalog is used, enforce compatibility modes in CI. Keep event payloads small; avoid copying sensitive data into many topics when a reference or limited projection is enough.

Retry, DLQ, and Exactly-Once ​

Use bounded retries with exponential backoff and jitter for transient failures. Send poison messages to a DLQ with failure reason, attempt count, first-seen time, and safe replay information.

Exactly-once usually applies only inside the broker transaction and consumer-offset boundary. External side effects such as databases, email, or payments still need idempotency keys, unique constraints, inbox/outbox, or deduplication. Never say “exactly-once” without stating its boundary.

Operations ​

Test graceful shutdown, in-flight acknowledgment, and replay during consumer deployment. If queue age breaches an SLO, inspect downstream saturation, batch size, partition balance, and backpressure rather than only adding workers.

See Synchronous vs Asynchronous Communication, Async Processing, and Stream Processing for implementation details.

Created by Eren Demir.