Order API commits an order and outbox record; a publisher sends an OrderPlaced event to fulfillment and reporting consumers
One business event, two independent consumers. The outbox closes the database-to-broker failure gap.

Try the pattern on an order flow

  1. Choose the owner. The order service validates the request and owns the order record.
  2. Save the event. Write an outbox record in the same transaction as the order; publish it separately.
  3. Decouple consumers. Fulfillment and reporting subscribe independently and track processed event IDs.
  4. Prove recovery. Pause the broker, replay a message, and verify no order or shipment is duplicated.

Example event contract (illustrative values):

{
  "eventId": "evt-1042",
  "eventType": "OrderPlaced",
  "schemaVersion": 1,
  "orderId": "order-1042",
  "occurredAt": "2026-10-11T10:00:00Z"
}

Request-response APIs

A caller sends a request and waits for a result, usually over HTTP. This fits user-facing reads and actions that need an immediate answer: a storefront checking availability, for example. Keep the contract stable and specify authentication, timeouts, rate limits, and error responses. A synchronous call ties the caller's availability and latency to the provider; do not use it for work that may take minutes or must survive an outage. For more on where an API boundary belongs, see point-to-point vs API-led integration.

Message queue / competing consumers

A producer places a command or work item on a queue; one of several consumers processes each delivery. This suits slow, variable-volume tasks such as generating invoices after an order is accepted. The queue absorbs bursts and lets workers scale independently. Design for at-least-once delivery, idempotent processing, bounded retries, and dead-letter handling. A successful enqueue means the work was accepted, not that it is already complete.

Publish-subscribe events

A publisher announces a business fact, such as OrderPlaced, to a broker or event stream. Multiple subscribers can react without the publisher knowing each one. Use this when inventory, fulfillment, and analytics each need to respond independently. Events should carry stable IDs and versioned meaning; consumers must handle duplicates and late delivery. Publishing an event is not the same as asking a particular service to do a task: use a command queue when one worker must own the action.

Content-based routing

A router inspects a message attribute and sends it to the right destination. For example, an order's region may determine which fulfillment queue receives it. Define routing rules and a destination for unknown values so new regions do not silently lose orders. If order matters, partition by a stable business key and document where ordering is guaranteed; routing alone does not preserve global order.

Message translation

A translator maps one system's fields and codes into a contract another system understands. A legacy ERP might identify products by internal SKU while a storefront uses a public product ID. Keep mappings versioned and test missing or unexpected values; do not turn a shared canonical model into a catch-all for every team's private fields. A translation boundary is especially useful during staged migrations.

Splitter and aggregator

A splitter divides a large request into independent work items; an aggregator collects related results. For example, a bulk order can be split into line-item availability checks and then assembled into one response using a correlation ID. Decide what counts as complete, set a timeout, and represent missing or failed parts explicitly. This pattern adds coordination state, so avoid it when a single bounded request is sufficient.

Batch transfer and ETL

A scheduled job extracts and transforms a bounded data set, then loads it into another system. This is often the simplest choice for daily finance reconciliation or a reporting warehouse that does not need instant updates. Define the cutoff time, schema, validation totals, and a safe rerun strategy. Large files should use checkpoints so one bad row does not require starting over; downstream consumers must know how fresh the data is.

Change data capture (CDC)

CDC reads changes from a database log and publishes inserts, updates, and deletes without repeatedly scanning entire tables. It can maintain a search index or a read model with lower lag than a nightly batch. CDC emits database changes, not necessarily meaningful business events: schema changes, transaction boundaries, initial snapshots, ordering, and deletes need explicit handling. Do not give downstream consumers unrestricted access to raw sensitive fields.

Anti-corruption layer

Put a translation boundary between a legacy system's concepts and a new application's domain model. For example, an ERP's stock codes and status flags can be mapped to a stable availability contract instead of leaking ERP fields into every commerce feature. This protects consumers during a gradual migration. The mapping has an owner and operational cost; keep it narrow enough to avoid becoming another monolithic hub.

Transactional outbox

When a service must update its database and publish an event, write the business change and an outbox record in the same local transaction. A separate publisher sends outbox records to the broker and marks them delivered. This avoids the gap where a database commit succeeds but publishing fails. Publication can still happen more than once, so consumers must deduplicate using a stable event ID; the outbox is not an exactly-once guarantee.

Saga / long-running workflow

A saga coordinates steps across services when a single database transaction cannot cover them. An order workflow might reserve stock, authorize payment, and arrange shipment. Record each step and define a compensating action for a failure, such as releasing reserved stock. Compensation is not always a perfect reversal, so handle partial completion and human review explicitly. Prefer a simple single-owner process if the workflow does not need distributed coordination.

Choose by the constraint

NeedStart withWatch for
Immediate answerRequest-response APITimeouts and provider availability
One worker completes a taskMessage queueRetries and duplicate effects
Several independent reactionsPublish-subscribe eventContract evolution and ordering
Periodic bulk movementBatch / ETLFreshness and safe reruns
Continuous data replicationCDCSchema changes and sensitive data
Database update plus eventTransactional outboxDuplicate publication
Multi-service business processSagaPartial failure and compensation

These patterns often combine: an API accepts an order, an outbox publishes the event, a queue assigns fulfillment work, and CDC builds a reporting view. Before implementation, agree on the source of truth, data classification, delivery semantics, monitoring, replay, and who owns each boundary. For the operational details, read the retry and duplicate-event how-to.