messaging supports five messaging patterns. Each pattern maps to specific broker primitives (exchanges and queues in AMQP, streams and subjects in NATS) using deterministic naming.
| Pattern | Style | Use Case |
|---|---|---|
| Event Stream | Pub/sub | Domain events, notifications, audit trails |
| Custom Stream | Pub/sub | Isolated event domains (audit, telemetry) |
| Service Request | RPC | Synchronous queries between services |
| Service Response | RPC | Reply channel for service requests |
| Queue Publish | Point-to-point | Work queues, task distribution |
The default and most common pattern. Publish domain events to a shared topic exchange; any number of services subscribe by routing key.
┌─────────────────┐
orders ──publish──> │ events │ ──Order.Created──> notifications
│ .topic.exchange │ ──Order.Created──> analytics
│ │ ──Order.*───────> audit
└─────────────────┘
| AMQP | NATS | |
|---|---|---|
| Exchange/Stream | events.topic.exchange (topic) |
events (JetStream stream) |
| Publish to | Exchange with routing key | Subject events.Order.Created |
| Consumer | Durable queue events.topic.exchange.queue.{service} |
Durable consumer {service} |
| Routing | AMQP topic routing (* single, # multi) |
NATS wildcards (* single, > multi) |
Durable consumers survive restarts. In AMQP, these use quorum queues. In NATS, durable pull consumers.
Transient consumers auto-delete on disconnect. Useful for temporary subscriptions, live dashboards, or debugging.
Subscribe to multiple event types with wildcards:
| Pattern | Matches |
|---|---|
Order.Created |
Exactly Order.Created |
Order.* |
Order.Created, Order.Updated, Order.Deleted |
Order.# (AMQP) / Order.> (NATS) |
Order.Created, Order.Item.Added, any depth |
# (AMQP) / > (NATS) |
Everything |
The spec handles wildcard translation between transports automatically.
Same as event-stream but on a named exchange instead of the default events exchange. Use this when events belong to a separate domain.
┌─────────────────┐
audit-writer ──publish──> │ audit │ ──*──> audit-reader
│ .topic.exchange │
└─────────────────┘
- Events that don't belong in the main
eventsstream - Separate retention policies (e.g., audit logs kept longer)
- Isolation between bounded contexts
- High-throughput streams that shouldn't compete with general events
Synchronous request-reply between services. The caller sends a request and waits for a response.
┌────────────────────────────────────┐
caller ──request──> │ billing.direct.exchange.request │ ──> billing handler
└────────────────────────────────────┘ │
┌────────────────────────────────────┐ │
caller <──response── │ billing.headers.exchange.response │ <───────┘
└────────────────────────────────────┘
| AMQP | NATS | |
|---|---|---|
| Request exchange | {service}.direct.exchange.request (direct) |
Core NATS request-reply |
| Request queue | {service}.direct.exchange.request.queue |
— |
| Response exchange | {service}.headers.exchange.response (headers) |
Built-in reply subject |
| Response queue | {service}.headers.exchange.response.queue.{caller} |
— |
In NATS, request-reply uses built-in response routing — no explicit response exchange needed.
The response half of a service request. This is declared automatically when you set up a request handler — you rarely configure it directly.
The response exchange uses headers type in AMQP, which routes replies back to the correct caller based on header matching.
Direct publish to a named queue. The sender picks the destination explicitly. No exchange routing involved.
task-creator ──publish──> [email-queue] ──> email-worker-1
──> email-worker-2
Use for work queues where:
- The sender knows the destination
- Multiple workers compete for messages (competing consumers)
- No fan-out needed
Do multiple services need this event?
├── Yes → Event Stream (or Custom Stream for isolated domains)
└── No
├── Need a response? → Service Request/Response
└── Fire-and-forget to a specific queue? → Queue Publish
| Fan-out | Response | Routing | Durability | |
|---|---|---|---|---|
| Event Stream | Yes | No | Topic wildcards | Durable or transient |
| Custom Stream | Yes | No | Topic wildcards | Durable or transient |
| Service Request | No | Yes | Direct | Durable |
| Queue Publish | No | No | None (direct queue) | Durable |