Data Consistency and Sync Patterns for Commerce

Once the reference architecture splits truth across systems — stock in the WMS, orders in the OMS, money in the ERP — every copy is a consistency liability. This page is the pattern set that keeps the copies honest, ordered from the mistake everyone makes to the safety nets everyone eventually adds.

The Dual-Write Problem and the Outbox

The root defect: a service writes its database and publishes an event as two separate operations. Whichever order you choose, a crash between them either loses the event (downstream never hears) or emits a lie (downstream hears about a write that rolled back). At any meaningful volume this is not hypothetical — it is a scheduled appointment.

The fix is the transactional outbox: write the state change and the outgoing event into the same database inside one ACID transaction (the event goes to an outbox table); a separate relay reads the outbox and publishes to the broker, marking rows sent. The guarantee: an event exists if and only if the commit happened. The relay is at-least-once — which is fine, because…

Idempotency Is the Other Half

Every consumer must make redelivery harmless (IntegrationPatternVocabulary, idempotent receiver; ExactlyOnceAndDeliveryGuarantees for why the transport can't save you). The two working techniques:

Change Data Capture (Debezium)

When a system won't tell you about changes (legacy ERP, a platform with weak webhooks), CDC reads the database's own replication log and turns row changes into events — no polling, no application modification, ordered and complete. Debezium (open source, Kafka Connect-based; also runs standalone via Debezium Server) is the default engine, with first-class Postgres and MySQL support.

Commerce uses where CDC shines: feeding the analytics warehouse without touching production query capacity; extracting events from systems you can't modify; auditing every change to money-adjacent tables. The caveat: CDC events are row deltas, not domain events — "orders.status: 'paid'→'allocated'" needs a translation layer before it means "OrderAllocated" to consumers. For systems you own, prefer the outbox (which emits real domain events); use CDC for the systems you don't.

The Oversell Window, Sized Honestly

Sellable stock on the storefront is a delayed projection of WMS truth; the delay is the oversell window, and it cannot be zero (even sub-second sync loses to two customers clicking simultaneously on the last unit). The mature posture is not eliminating the window but engineering it:

  1. Measure the loop latency (WMS change → storefront update) — this is a monitorable SLO, not a vibe.
  2. Buffer the fast movers — safety stock thresholds per velocity class: show "in stock" only above N units for SKUs that sell hourly. (InventoryManagementStrategies has the velocity math.)
  3. Reserve at checkout for the contested case — short-TTL holds on add-to-cart or checkout-start for flash-sale/low-stock SKUs; a global reservation system for all SKUs is enterprise-tier machinery most stacks don't need.
  4. Script the miss — an oversell path that auto-triggers apology + refund/backorder choice + priority restock beats any amount of pretending. The metric that matters is oversells handled badly, not oversells.

Reconciliation: The Safety Net That Catches Everything Else

Every pattern above reduces drift; none eliminates it (bugs, manual edits, crashed relays, partner systems lying). Scheduled reconciliation is mandatory at every tier — it is the pattern that turns silent corruption into a morning alert:

Design rule: reconciliation compares systems of record against projections per the ownership table — if a reconciliation can't say which side is right, the ownership table has a hole in it.

Read Models and CQRS, Applied

The storefront's sellable-stock number, the ops dashboard, the search index — these are read models: purpose-built, eventually-consistent projections fed by events (CqrsPattern). Naming them as such clarifies obligations: a projection may lag (display its staleness honestly), must be rebuildable from the source of truth (the enterprise-tier argument for a replayable log — IntegrationBackboneChoices), and must never be written to directly.

Pattern-by-Tier Summary

TierConsistency kit
GarageOne ACID database doing the work + nightly reconciliation query + oversell script
GrowthOutbox + idempotent consumers + measured sync loop + daily reconciliation with alerts
EnterpriseAll of the above + CDC to warehouse + replayable log + continuous reconciliation dashboards

See Also