Skip to content

Core concept · Reliability

Events, queues, and outbox

These mechanisms are not interchangeable. Choose from the consistency requirement, delivery guarantee, latency expectation, and ownership boundary.

Decision table

MechanismBest fitCaller waits?Failure model
Direct callImmediate result within one application boundaryYesFailure returns to caller
In-process eventIndependent reaction where best-effort delivery is acceptableUsually noLost on crash unless separately persisted
Durable queueDeferred, bursty, retryable, or resource-heavy workNoDuplicate and delayed delivery must be expected
Transactional outboxDatabase write and durable publication must agreeNoPublisher retries committed outbox records

An event names a completed fact such as OrderPlaced. A command asks for work such as CapturePayment. Do not rename a synchronous command as an event to avoid defining its failure contract.

Use queues for operational separation

A durable queue is useful when work can happen later, has a different capacity profile, must absorb bursts, or should retry independently from the request. It also introduces queue lag, duplicate delivery, poison messages, retention, worker deployment, and shutdown concerns.

Every consumer should define:

  • an idempotency key or deduplication strategy;
  • bounded concurrency and payload size;
  • retryable versus terminal errors;
  • exponential backoff with jitter and a retry limit;
  • dead-letter or operator recovery behavior;
  • trace, metric, and structured-log correlation;
  • safe drain or requeue behavior during shutdown.

Close the dual-write gap with an outbox

If a business write and message publication must not diverge, write the business state and an outbox record in the same database transaction:

text
request transaction
  ├─ update aggregate
  └─ insert outbox record

publisher
  ├─ claim committed records
  ├─ publish message
  └─ mark published (or retry)

The outbox closes the gap between committing data and publishing a message, but it does not create exactly-once side effects. The publisher or broker can still deliver more than once, so consumers remain idempotent.

Preserve ownership

Architecture decides which capability owns the fact and its transaction. Object design makes the event immutable and meaningful. Runtime design chooses in-process or durable transport, serialization, retries, backpressure, observability, and recovery.

Use Scaling and reliability for operational rules and Pattern catalog for selection forces.

Open-source guidance for deliberate NestJS engineering.