Messaging Patterns
Messaging patterns define how components communicate asynchronously — decoupling producers from consumers in time, space, and implementation. They are fundamental to event-driven architectures, microservices, and any system that needs to handle work without blocking the caller.
Pattern Comparison
| Pattern | Delivery | Coupling | Consumers | Use Case |
|---|---|---|---|---|
| Publish-Subscribe | Fan-out to all subscribers | Low (topic-based) | Many | Notifications, event broadcasting |
| Message Queue | Point-to-point, one consumer | Low | One (competing consumers) | Work distribution, task processing |
| Event Bus | In-process fan-out | Low (in-process) | Many (in-process) | Modular monolith, plugin systems |
| Request-Reply | Correlated request/response | Medium | One | RPC over messaging, async queries |
| Dead Letter Queue | Failed message routing | Low | One (error handler) | Error handling, poison messages |
Decision Guide
Is communication within a single process?
├─ Yes → Event Bus
└─ No → Is it broadcast (many consumers)?
├─ Yes → Publish-Subscribe
└─ No → Is it fire-and-forget work?
├─ Yes → Message Queue
│ └─ Handle failures → Dead Letter Queue
└─ No → Need a response?
└─ Yes → Request-Reply
Combining Patterns
Messaging patterns layer naturally:
Producer
└─ Publishes to Topic (Pub/Sub)
├─ Subscriber A → Message Queue (work distribution)
│ └─ Failures → Dead Letter Queue
├─ Subscriber B → Request-Reply (enrichment)
└─ Subscriber C → Event Bus (in-process dispatch)
Key Principles
- Decouple producers and consumers — Neither should know about the other's implementation.
- Make messages self-describing — Include enough context to process without callbacks.
- Handle failures explicitly — Dead letter queues prevent poison messages from blocking processing.
- Idempotency — Consumers must handle duplicate messages gracefully.
- Ordering matters — Know whether your messaging system guarantees order, and design accordingly.