Skip to content

CQRS, Event Sourcing, Outbox, and Sagas

The database chapter supplied local transactions, durable identities, and versioned projections. This stage decides how much architectural separation those mechanisms should support. CQRS means command/query responsibility segregation: separating changes from reads. Event sourcing changes the authoritative storage model; an outbox preserves publication intent; a saga coordinates local transactions across owners. Read the spectrum first, compare the three workloads, and then follow the operating contract for each pattern.

Evidence notation: C identifies a claim in the claim register, A a dated AWS source, F a foundational source, and CS a finding in the repository case study. The source index supplies the full source details. These labels are lookup aids, not facts to memorize.

The senior design move is not “add CQRS.” It is to name the authoritative business state, measure why the read and write paths differ, and add only the separation whose freshness, ordering, recovery, and cost can be operated. CQRS does not require two databases. CQRS does not require event sourcing. Event sourcing, transactional outbox, and sagas solve different problems and can be adopted independently (C35,C97; F06,F36; A113, retrieved 2026-08-22).

This chapter consumes the authority and guarantee vocabulary from event-driven foundations, the service boundaries from the AWS messaging guide, the ordering/replay/backlog limits from the Kinesis deep dive, and AP12 ledger/risk authority, AP07 derived portfolio, AP16 semantic outbox, inbox/version/gap/rebuild, and amplification boundaries from the DynamoDB deep dive. It does not turn a transport, cache, search index, portfolio, or analytics view into financial authority.

The archived compilation of material attributed to Greg Young separates command intent from query information, while Fowler warns that CQRS adds risky complexity when applied indiscriminately. AWS describes separate command/query models and possible database combinations; that is one implementation family, not a requirement (C35,C97; F06,F36; A113, retrieved 2026-08-22).

StepSeparationState shapeConsistency and recoveryEarn it whenStop when
1Separate code pathsCommand handlers and query handlers use one domain/state model and one store.Queries can use the same committed state; ordinary backup/restore applies.Different authorization, validation, or API contracts make code clearer.One CRUD path is clearer and no policy differs.
2Same-store command/query modelsDifferent DTOs, SQL/DynamoDB access paths, or base-table item shapes share one authority.Same transaction domain is possible; indexes/replicas still carry their documented lag.Measured query shape differs but another data pipeline is not justified.Index/write amplification or query coupling misses the SLO.
3Authoritative store + projectionCommands commit authoritative state; an outbox/CDC relay builds one rebuildable view.Eventual freshness, version/inbox controls, full rebuild, and reconciliation become mandatory.A measured read pattern cannot meet latency/capacity/isolation goals on authority.Lag or rebuild risk is unacceptable for the business decision.
4Specialized read storesSeparate projections serve search, cache, portfolio, and analytics access patterns.Each store has its own freshness, overload, schema, rebuild, and cutover contract.Distinct query engines produce enough avoided work to pay fan-out and operations.Polyglot ownership and drift cost exceed avoided read work.
5Event-sourced command modelThe authoritative command state is an append-only per-aggregate event sequence; current state and reads are derived.Expected-version append, snapshots, evolution, replay isolation, store recovery, and temporal semantics are first-class.Authoritative history, temporal reconstruction, or domain transition evidence earns it.CRUD state plus an audit/ledger/outbox meets the requirement more safely.

Step 5 is not “more mature” than Step 3. It changes authority and failure recovery. A team can use event sourcing without a specialized read database, or CQRS with ordinary CRUD state.

Inference: complexity is earned only by measured access patterns, consistency/freshness SLOs, recovery objectives, and total operating cost—not by a pattern ladder (C35,C48,C97; F06,F19,F20,F36).

Problem / why. One model can force command invariants and high-volume query shapes to compete for code, capacity, schema, and release cadence. Separation earns its place only when traces show material read work, latency, or isolation that the simpler step cannot meet (C35,C48,C97,C102; F06,F19,F20).

Guarantee scope and stopping boundary. Steps 1–2 can keep one committed state boundary. Steps 3–5 introduce derived freshness and recovery obligations; none changes the named command authority or makes a cross-domain query an atomic snapshot. CQRS never supplies delivery, order, or one-time external effects by itself (C35,C41,C46,C97; F01,F06).

Concrete workload. The validated input/derivation table in CQRS break-even model is the concrete economic case; the repository variants separately use their visible peak and query workloads rather than treating the economic ratio as production measurement (C49,C102; F19,F20).

Fit and poor fit. Fit is a measured SLO/cost/isolation miss with an owned projection contract and positive sensitivity. Poor fit is low volume that cannot amortize ownership, strongly current monetary queries, or a team unable to rebuild and reconcile; remain at the lowest sufficient spectrum step.

Failure and recovery. A read-side outage must not stop authoritative commands: return a versioned receipt/pending state, shed projection-dependent features, repair gaps, rebuild vNext, reconcile, and cut over. If commands depend on the view, the split has crossed its stopping boundary (C44,C48,C100; F13,F14).

Variable cost. Count transport, every projection/index write, query/store compute, bytes and retention, cache/search nodes, replay bandwidth, and reconciliation work per write/read—not merely the cheap serving read (C49,C95, C102; A45,A46,A78, retrieved 2026-08-22).

Fixed ownership. One named team owns projection schemas, compatibility, lag/error alarms, rebuild tooling, reconciliation evidence, capacity tests, runbooks, security, and on-call. This fixed burden exists even during a quiet month.

Metric, threshold, response. Measure command p99, query p99, read work avoided, projection p95/max age, gap age, rebuild RTO, discrepancy count, and CU net benefit. At 30 seconds of projection age or any unresolved monetary break, disable projection-dependent actions, fall back to authority/pending, isolate repair, and page the owner (C43,C48,C68; A36, retrieved 2026-08-22).

Anti-pattern to reject. “CQRS means two eventually consistent databases,” or choosing Step 4/5 before measuring read work and recovery ownership.

Follow-up questions. Which query/SLO fails at the current step? What is the authority during lag, how does read-your-writes work, and who proves a rebuild?

Two-minute defense. “I start with separate command/query code against one authority, then move only the measured read path. The projection is stale and rebuildable, so the receipt carries a version, overload degrades reads rather than invariants, and the extra store must beat transport, fan-out, recovery, reconciliation, and on-call cost under sensitivity.”

CQRS asks whether command and query responsibilities use different models. Event sourcing asks whether the authoritative state is a replayable event sequence. These choices form a two-dimensional matrix:

Command authoritySame query modelSeparate query model
Current-state CRUDOrdinary service, perhaps code-path separationCQRS with same-store DTOs or asynchronous projections
Event sequenceReconstruct aggregate and answer limited queries directlyEvent-sourced command model plus one or more projections

Publishing OrderAccepted after updating an order row is event notification or event-carried state transfer; it is not event sourcing. Conversely, an event-sourced aggregate can initially serve a small query from its reconstructed state without a second database (C36,C58,C98; F07,F29,F30,F36; A114, retrieved 2026-08-22).

Inference: the repository starts at CQRS-lite: accepted order state, AP12 cash/securities reservations and ledger postings, execution state, and risk limits are authoritative; AP07 portfolio, search, cache, notification, and analytics are rebuildable projections (C46,C91,C97; F08,F17,F21). A strong read does not promote a projection to authority. Strongness describes a store/API read option, not the business provenance of the data (C82,C92; A19,A32,A34,A35, retrieved 2026-08-22).

Architecture contract shared by all variants

Section titled “Architecture contract shared by all variants”

The spectrum describes possible separation, not a progression that every system should climb. The variants below compare different workloads using the same questions: who decides, how stale may reads become, what does a duplicate do, and how is a complete rebuild proved? AP references point back to the DynamoDB access patterns; they keep those authority and cost decisions consistent across the designs.

The machine-readable block is the source for the rendered table. Every variant must state authority, lag/overload, order, read-your-writes, duplicate handling, rebuild, recovery/reconciliation, variable cost, fixed operational cost, poor fit, and quantified workload.

These fields preserve the reviewed ordering/backlog, authority, projection, rebuild, and economic boundaries (C41,C44,C46,C49,C95,C100,C102; F13,F14,F19,F20; A36,A45,A46, retrieved 2026-08-22).

Model details · task7 variants
VARIANT|CQRS-lite|accepted order, AP12 ledger/reservation, and risk state|AP07 portfolio p95 under 3 s; at 30 s stop projection-dependent UX and fall back to authority or pending status|per orderId transition and per accountId ledger sequence; no global order|return command receipt with orderId and version; strong base read or wait up to 2 s for projectionVersion at least receipt version|transactional inbox eventId plus conditional next aggregateVersion; duplicate returns stored receipt|PITR export plus outbox/event catch-up into versioned AP07 namespace; control totals then blue-green cutover|repair AP16 outbox and parked gaps; reconcile orders, fills, postings, reservations, and AP07 by batch|DynamoDB transaction and GSI units; Streams/Lambda relay; projection reads/writes; export and replay|projection ownership, alarms, schema/upcaster, rebuild drills, reconciliation and on-call|commands that must decide from AP07 or workload stays inside one store SLO|5,000 accepted orders/s planning peak; 2,000 AP06 queries/s; one portfolio projection
VARIANT|Read-heavy brokerage|order, execution, AP12 ledger/reservation, and risk command stores|portfolio/search/cache p95 under 2 s normal and under 30 s recovery; freeze stale widgets, serve receipt overlay, and shed analytics before authority|per accountId or orderId source version; Kinesis partition key preserves only its documented shard scope; cross-key facts reconcile|receipt overlay and minVersion query token; wait up to 2 s then return pending or read authoritative API|one inbox eventId and sourceVersion condition per projection; stale and duplicate no-op, gaps park|export authoritative domains to S3 manifest; isolated portfolio-vNext, OpenSearch-vNext, and cache warm; catch up and cut over separately|SQS DLQs and retained Kinesis/S3 replay feed isolated rebuild; reconcile counts, money totals, versions, and search coverage|transport fan-out; four projection/index writes; OpenSearch, ElastiCache, S3 and query compute/storage|four-store ownership, schema compatibility, security, lag/on-call, game days and capacity reservations|read/write ratio below modeled threshold or customer action needs strongly current cross-domain state|1,000 commands/s peak; 50,000 reads/s peak; 4 projections; 2 s normal freshness and 30 s overload budget
VARIANT|Write-heavy execution stream|accepted order and reservation/risk state before execution; matcher and authoritative execution/ledger records after execution|status projection p95 under 5 s and hard alarm at 60 s; preserve ingestion by pausing analytics, rate-limit low-priority commands, and drain within retention|instrumentId sequencing lane for matcher and accountId or orderId versions around it; no cross-shard total order|command receipt and authoritative status endpoint; never infer acceptance or fill from stream position|producer eventId plus authoritative expected version; projector inbox; deterministic fill identity and effect receipt|retain authoritative executions/outbox beyond Kinesis window; bulk load compacted vNext status then replay Kinesis or S3 catch-up|separate producer ambiguity, iterator lag, poison, and sink runbooks; reconcile accepted orders to executions, postings, and published effects|minimal outbox/transport plus limited status and audit writes; batching and storage; replay bandwidth|long-lived matcher capacity, shard/key design, lag alarms, archive, reconciliation and incident ownership|hard invariant spans asynchronous consumers or Lambda latency/runtime is assumed suitable for matching|5,000 average and 8,000 peak events/s at 700 B; 13-shard planning example; 900 s outage and 18.75 min ideal drain
VariantAuthoritative stateConsistency/freshness lag and overload policyOrdering scopeRead-your-writesDuplicate handlingRebuild methodRecovery/reconciliationVariable costOperational costPoor fitQuantified workload
CQRS-liteaccepted order, AP12 ledger/reservation, and risk stateAP07 portfolio p95 under 3 s; at 30 s stop projection-dependent UX and fall back to authority or pending statusper orderId transition and per accountId ledger sequence; no global orderreturn command receipt with orderId and version; strong base read or wait up to 2 s for projectionVersion at least receipt versiontransactional inbox eventId plus conditional next aggregateVersion; duplicate returns stored receiptPITR export plus outbox/event catch-up into versioned AP07 namespace; control totals then blue-green cutoverrepair AP16 outbox and parked gaps; reconcile orders, fills, postings, reservations, and AP07 by batchDynamoDB transaction and GSI units; Streams/Lambda relay; projection reads/writes; export and replayprojection ownership, alarms, schema/upcaster, rebuild drills, reconciliation and on-callcommands that must decide from AP07 or workload stays inside one store SLO5,000 accepted orders/s planning peak; 2,000 AP06 queries/s; one portfolio projection
Read-heavy brokerageorder, execution, AP12 ledger/reservation, and risk command storesportfolio/search/cache p95 under 2 s normal and under 30 s recovery; freeze stale widgets, serve receipt overlay, and shed analytics before authorityper accountId or orderId source version; Kinesis partition key preserves only its documented shard scope; cross-key facts reconcilereceipt overlay and minVersion query token; wait up to 2 s then return pending or read authoritative APIone inbox eventId and sourceVersion condition per projection; stale and duplicate no-op, gaps parkexport authoritative domains to S3 manifest; isolated portfolio-vNext, OpenSearch-vNext, and cache warm; catch up and cut over separatelySQS DLQs and retained Kinesis/S3 replay feed isolated rebuild; reconcile counts, money totals, versions, and search coveragetransport fan-out; four projection/index writes; OpenSearch, ElastiCache, S3 and query compute/storagefour-store ownership, schema compatibility, security, lag/on-call, game days and capacity reservationsread/write ratio below modeled threshold or customer action needs strongly current cross-domain state1,000 commands/s peak; 50,000 reads/s peak; 4 projections; 2 s normal freshness and 30 s overload budget
Write-heavy execution streamaccepted order and reservation/risk state before execution; matcher and authoritative execution/ledger records after executionstatus projection p95 under 5 s and hard alarm at 60 s; preserve ingestion by pausing analytics, rate-limit low-priority commands, and drain within retentioninstrumentId sequencing lane for matcher and accountId or orderId versions around it; no cross-shard total ordercommand receipt and authoritative status endpoint; never infer acceptance or fill from stream positionproducer eventId plus authoritative expected version; projector inbox; deterministic fill identity and effect receiptretain authoritative executions/outbox beyond Kinesis window; bulk load compacted vNext status then replay Kinesis or S3 catch-upseparate producer ambiguity, iterator lag, poison, and sink runbooks; reconcile accepted orders to executions, postings, and published effectsminimal outbox/transport plus limited status and audit writes; batching and storage; replay bandwidthlong-lived matcher capacity, shard/key design, lag alarms, archive, reconciliation and incident ownershiphard invariant spans asynchronous consumers or Lambda latency/runtime is assumed suitable for matching5,000 average and 8,000 peak events/s at 700 B; 13-shard planning example; 900 s outage and 18.75 min ideal drain

Assume 1,000 commands/s peak, 50,000 reads/s peak, four independently owned projections, normal freshness p95 below two seconds, and a 30-second overload budget. The query side denormalizes the portfolio/API view in DynamoDB, uses OpenSearch for discovery, ElastiCache for disposable hot data, and S3/Athena for analytics. These stores match different APs and cost shapes; none authorizes money or risk (C46,C52,C92; A32,A34,A35,A78,A75, retrieved 2026-08-22).

Diagram text source
flowchart LR
C[Client] --> A[API Gateway and command Lambda]
A --> T[(Order plus AP12 authority and semantic outbox)]
T --> S[DynamoDB Streams relay]
S --> K[Kinesis retained fan-out]
K --> P[Portfolio projector]
K --> O[Search projector]
K --> H[Analytics projector]
P --> D[(DynamoDB portfolio vN)]
O --> OS[(OpenSearch index vN)]
P --> E[(ElastiCache disposable cache)]
H --> B[(S3 then Athena)]
C --> Q[Query API with minVersion]
Q --> D
Q --> OS
Q --> E
Q -. receipt overlay or authority fallback .-> T

Inference: write amplification is deliberate: one authoritative commit, one semantic relay path, and four projection/index writes are paid because 50 reads occur per peak write and the query engines avoid repeated joins and scans (C49,C95,C102; A45,A46,A78, retrieved 2026-08-22; F19,F20). Measure actual read work avoided; do not count a cache hit and underlying avoided query twice.

Inference: read-your-writes returns {orderId, acceptedVersion}. The query accepts minVersion; it either sees a projection at or beyond that version, overlays the receipt/pending command state, waits at most two seconds, or routes a bounded strong base-table read. It never claims the whole cross-domain portfolio is a transactional snapshot. Under overload, freeze stale widgets with an “as of” time, shed analytics and refresh traffic, and protect command/ledger capacity (C44,C48,C61; F13,F14).

Inference: residual risks are cross-account skew, OpenSearch mapping failures, stale cache invalidation, four schema migrations, replay load, and a user seeing mutually different “as of” versions. Poor fit: the measured read/write ratio is below break-even, a query must decide a current monetary invariant, or the team cannot rebuild and operate four stores. Track transport failures, per-projection lag, gap age, inbox duplicates, DLQ/redrive outcomes, rebuild progress, and business reconciliation separately; a green service dashboard is not proof of correct financial state (C43,C68).

Use the reviewed Kinesis workload (source model 5): 5,000 records/s average, 8,000/s peak, 700 B average payload, 13 provisioned-shard planning result, two full-stream consumers, a 900-second outage, and 18.75-minute ideal drain at 9,000 commits/s. Kinesis order and replay stay shard/retention scoped; producer ambiguity, parallel consumers, poison records, and lag still require stable identity, versions, isolation, and reconciliation (C41,C72-C77; A46,A96-A99, retrieved 2026-08-22).

Diagram text source
flowchart LR
C[Client command] --> G[Acceptance API]
G --> R[(Order reservation risk authority and outbox)]
R --> L[Outbox relay]
L --> K[Kinesis by evidenced key]
K --> M[Long-lived matcher and sequencer]
M --> X[(Execution and ledger authority plus outbox)]
X --> KS[Kinesis status and audit feed]
KS --> P[(Limited status projection)]
KS --> S3[(S3 durable replay archive)]
R --> Q[Authoritative status API]
X --> Q

The matching loop remains outside the default Lambda/serverless design. Use long-lived ECS/Fargate/EC2 or another evidenced sequencer/runtime for matching; Lambda/serverless surrounds it for command acceptance, workflows, projections, notification, reconciliation, and audit (C49,C53; A44,A58,A59,A60, retrieved 2026-08-22; F09).

Inference: a random Lambda execution simulation, managed scaling label, or stream throughput number does not prove deterministic latency or sequencing fitness (CS12).

Keep synchronous fan-out minimal: acceptance transaction, reservation/risk decision, and semantic outbox. Batch transport and projection writes only where their latency budget allows. Partition the matcher by an evidenced sequencing scope such as instrumentId; preserve per-order/account versions around that scope, and coordinate/reconcile cross-key monetary effects. One hot aggregate still serializes; reject, queue fairly, or redesign the aggregate rather than pretend more shards split it (C34,C44,C75,C87; A95,A105, retrieved 2026-08-22).

Inference: Kinesis is the default buffer in the reviewed Kinesis planning contract (source model 5). Choose Amazon MSK instead only when Kafka protocol/ecosystem, consumer-group, retention, or transaction requirements earn its cluster and operational model—not as a generic “more scalable queue.” For either choice, batching changes request efficiency rather than aggregate authority, and partition/key skew, consumer lag, replay, poison isolation, and sink capacity remain explicit (C29,C42,C44; A14,A30, retrieved 2026-08-22).

Inference: at 60 seconds of status lag, pause analytics, isolate replay, rate-limit lower- priority commands, and preserve authoritative ingestion. Async compaction builds a new status generation from authoritative executions plus retained catch-up; it does not compact away audit/ledger authority. Poor fit: a hard invariant would depend on asynchronous consumers, the sequencing scope is undefined, or Kinesis/S3 retention cannot cover detection plus rebuild plus drain (C44,C68, C77; A36,A98,A99, retrieved 2026-08-22).

The default keeps the reviewed DynamoDB transaction (source model 6): accepted order, durable idempotency result, uniqueness sentinel, and semantic outbox; after commit the named GSIs add ordinary asynchronous writes. AP12 ledger/reservation/risk state is authoritative, while AP07 portfolio is a versioned projection (C83,C90,C91, C95; A100,A101,A104, retrieved 2026-08-22).

The named worked route is not “one write”: four transaction-targeted base items consume 12 modeled units and three asynchronously maintained GSI entries add 3, for 15. AP16's complete create/claim/complete/retry/reschedule lifecycle models 40,440 base write units/s plus 10,240 sparse READY-index units/s, or 50,680 total. A CQRS decision must preserve those transaction/index, insert/delete/key-move, retry, and rebuild writes rather than count only the serving projection (C95; A100,A104,A105, retrieved 2026-08-22).

Diagram text source
flowchart LR
C[Client] --> A[Order command Lambda]
A --> D[(DynamoDB order AP12 authority plus AP16 outbox)]
D --> S[DynamoDB Streams relay]
S --> B[EventBridge routing]
B --> Q[SQS owned work]
Q --> P[Idempotent AP07 projector]
P --> V[(Portfolio vN rebuildable)]
C --> R[Read API]
R --> V
R -. minVersion fallback .-> D
D --> J[Reconciliation and repair]
V --> J

At the planning peak, this chapter reuses the DynamoDB model 6 inputs of 5,000 accepted orders/s, 2,000 AP06 queries/s, and one AP07 projection. Normal AP07 p95 freshness is under three seconds; at 30 seconds the product disables projection-dependent actions, displays pending/as-of state, and falls back to authoritative reads where bounded.

Inference: add a specialized store only after query traces show the current model misses its SLO, the break-even remains positive under sensitivity, and an owner proves rebuild/reconciliation (C48,C97,C102).

Residual risk remains around cross-item Stream interleaving, EventBridge target delivery, projector retry/gaps, and restore beyond 24 hours. The semantic outbox represents one business event; AP09 inbox plus source version protects AP07; a PITR export and catch-up build portfolio-vNext; control totals and a blue/green pointer switch complete recovery (C84,C88,C96,C99,C100; A102,A106, retrieved 2026-08-22).

Outbox and CDC: the atomicity gap each variant closes

Section titled “Outbox and CDC: the atomicity gap each variant closes”

All three variants depend on a trustworthy handoff from committed state to consumers. This is the common mechanism beneath their different service diagrams. Compare raw change data capture (CDC) with an explicit semantic outbox, and keep the local transaction's promise separate from relay acknowledgements and downstream effects.

VariantAtomicity gap closedWhat remains
DynamoDB semantic outbox item + Streams relayTransactWriteItems commits authoritative items and one business publication-intent item together inside its documented account/Region transaction boundary. The relay can later publish that envelope.Transaction item changes can interleave in Streams; Lambda can repeat; publication can be ambiguous; consumer effects need event ID, inbox/version guards, replay and reconciliation (C83,C84,C99; A20,A21,A101,A102,A115, retrieved 2026-08-22; F04,F11).
Raw DynamoDB Streams CDCIt removes an application poll between a committed item change and observation of that change. It does not create one semantic envelope for a multi-item transaction or preserve beyond 24 hours.Consumers infer meaning from storage rows, transaction changes can interleave, duplicates occur, and long rebuild needs another source. Use only when row-level CDC is the contract (C84,C96,C99; A21,A102,A106, retrieved 2026-08-22).
Relational outbox polling/CDCOne relational transaction commits business rows and the outbox row, closing local state versus publication-intent dual write. Polling or transaction-log CDC relays committed rows.Poll/CDC checkpoints, ambiguous broker acknowledgements, repeat publication, cleanup retention, schema evolution, consumer idempotency, and reconciliation remain (C38,C40,C99; A115, retrieved 2026-08-22; F04,F11).
Direct database/broker dual writeNo atomicity gap is closed: database-first can commit without publish; broker-first can publish without state; timeout makes either result ambiguous. Direct database/broker dual write is unsafe as the default.A repair log discovered from authority can reduce loss, but it has become an outbox/reconciliation design. Never call two SDK successes a transaction (C38,C99; F04).

Inference: an outbox is not a forever event store. Retain unpublished/repair evidence past the maximum relay outage, record attempts and broker receipts, delete/archive only under a proven lifecycle, and reconcile “authoritative event expected” to “target business effect observed.” It closes one local atomicity gap; duplicates, replay, idempotency, and reconciliation remain (C38,C43,C99; F04,F11).

Problem / why. A command can commit state and crash before publishing, or publish and then roll back/timeout. One semantic outbox item committed with the state makes publication intent durably discoverable without a broker/database distributed transaction (C38,C99; F04; A115, retrieved 2026-08-22).

Guarantee scope and stopping boundary. The guarantee stops at the local database transaction containing authoritative state and outbox intent. Relay read, broker acknowledgement, target delivery, consumer commit, and external effect remain retryable or ambiguous and require stable identity, inbox/version guards, receipts, and reconciliation (C38,C40,C84,C99; F04,F11).

Concrete workload. Reuse the reviewed 5,000 accepted orders/s case: four transaction-targeted items consume 12 modeled base units, three asynchronous GSI entries add 3, and the balanced AP16 relay lifecycle totals 50,680 modeled write units/s including READY-index movement. The numbers are planning inputs, not a universal outbox price (C95; A100,A104,A105, retrieved 2026-08-22).

Fit and poor fit. Fit is a local commit whose publication must survive a crash. Poor fit is row-level analytics CDC that needs no semantic envelope, an already-authoritative append log with a proven publisher, or a cross-service invariant falsely being hidden behind an outbox.

Failure and recovery. Persist relay lease, attempt, broker response, and next-due state; recover expired IN_FLIGHT items to READY; retry ambiguous publication under the same event ID; park poison; replay from retained outbox/ export; reconcile expected events to business effects before cleanup (C43,C90, C99; F04,F11).

Variable cost. Count extra transactional item bytes/units, sparse READY index insert/delete/move work, Streams reads, Lambda/relay invocations, broker requests/bytes, retention, retry/replay, DLQ storage, and reconciliation.

Fixed ownership. The producing domain owns the semantic schema and outbox lifecycle; the relay owner owns leases/checkpoints/broker ambiguity; each consumer owns inbox/effects; operations owns alarms, repair, retention, and a reconciliation runbook.

Metric, threshold, response. Measure oldest READY age, READY/IN_FLIGHT counts, expired leases, publish failures/ambiguities, duplicate inbox hits, DLQ age, and expected-versus-observed effect breaks. At 30 seconds oldest READY age or any missing financial effect, page, protect relay capacity, stop cleanup, repair leases/gaps, replay idempotently, and reconcile (C43,C68,C99; A36, retrieved 2026-08-22).

Anti-pattern to reject. Publishing directly after a database commit and calling the pair atomic, or treating a raw change row as a stable business event.

Follow-up questions. Which local atomicity gap is closed? Where are relay checkpoints, event identity, repair retention, consumer idempotency, and proof that committed intents reached every required sink?

Two-minute defense. “The outbox closes only state-versus-publication-intent atomicity in one database transaction. The relay may publish twice and the consumer may run twice, so event identity, inbox/version checks, effect receipts, retained repair state, and reconciliation—not broker wording—prove the outcome.”

Idempotent projections and controlled rebuilds

Section titled “Idempotent projections and controlled rebuilds”

For each projection event, carry eventId, aggregateId, aggregateVersion, eventType, schemaVersion, occurred/recorded time, causation/correlation, and producer. In one target transaction or conditional boundary:

  1. create an inbox receipt keyed by stable eventId;
  2. require incomingVersion = currentVersion + 1 for the aggregate;
  3. apply the projection mutation and store the new source/projection version;
  4. return the stored effect receipt for a duplicate;
  5. advance a source checkpoint only after the target commit.

Inference: stale versions no-op with evidence; the next version applies; a future version parks in a gap queue and alarms by age/key. Deduplication alone does not order events, and a version check alone does not deduplicate different event IDs (C40,C41,C93,C100; F01,F02,F11).

Inference: a controlled rebuild is blue/green, not in-place (C88,C96,C100; A106, retrieved 2026-08-22):

  1. declare target projection/schema version and a source watermark;
  2. export authoritative state/events with a manifest and control totals;
  3. load an isolated vNext namespace with notifications/payments disabled;
  4. catch up from the watermark using separate capacity, checkpoints, versions, and rate limits so live traffic retains headroom;
  5. compare counts, aggregate versions, cash/security totals, missing IDs, and sampled query equivalence;
  6. switch readers atomically or by bounded cohort, retain vOld for rollback, then archive evidence.

Rebuild completion is not merely “iterator age zero.” Reconciliation proves the served result against authority. Backfill identity must be stable across restarts, and backfill traffic must be isolated from live projections.

Projection and rebuild operational contract

Section titled “Projection and rebuild operational contract”

Problem / why. A projection precomputes one expensive read shape and isolates query demand, but it deliberately duplicates authoritative facts and can be stale, duplicated, out of order, or structurally incompatible after a schema change (C41,C46,C100; F01,F02).

Guarantee scope and stopping boundary. Inbox identity plus the conditional next source version protects one named projection mutation. It does not make cross-aggregate views atomic, prove checkpointed external effects, or permit a portfolio/search/cache row to authorize balance, reservation, risk, order, or posting decisions (C40,C41,C46,C100; F01,F11).

Concrete workload. CQRS-lite assumes 5,000 accepted source changes/s, one portfolio projection, 2,000 AP06 queries/s, normal p95 freshness below three seconds, and degradation at 30 seconds. The read-heavy case assumes 1,000 commands/s, 50,000 reads/s, four projections, two-second normal and 30-second recovery freshness.

Fit and poor fit. Fit is a stable access pattern whose avoided read work pays write/rebuild/on-call burden and tolerates its declared as of time. Poor fit is a hard invariant, ad hoc query churn, no complete retained source, or no capacity/owner for an isolated rebuild.

Failure and recovery. Duplicate/stale versions no-op with receipts; future versions park and alarm. Export authority at a watermark, load vNext with external effects suppressed, catch up on isolated capacity, compare versions/ counts/money totals, canary readers, and blue/green cut over with vOld rollback (C88,C96,C100; A102,A106, retrieved 2026-08-22).

Variable cost. Count one inbox/conditional projection mutation per event, target index/storage/cache/search work, source reads, gap/DLQ retention, full backfill and catch-up compute/I/O, validation queries, and temporary blue/green double capacity.

Fixed ownership. A projection owner maintains schemas/upcasters, source-to- target version rules, checkpoints, gap queues, rebuild manifests, cutover/ rollback tooling, control totals, SLOs, capacity isolation, and on-call.

Metric, threshold, response. Track p95/max source age, gap oldest age, duplicate/stale counts, checkpoint minus committed target version, rebuild ETA, control-total breaks, and query fallback. At 30 seconds age or any version/money break, freeze affected reads/actions, isolate the key, backfill/replay, compare authority, and cut over only after zero unexplained breaks (C43,C48,C68,C100; A36, retrieved 2026-08-22).

Anti-pattern to reject. Rebuilding a live projection in place, overwriting newer versions, or using a strong read of a derived view as monetary authority.

Follow-up questions. What source covers a full rebuild, how are gaps parked, what watermark makes catch-up safe, and which totals authorize blue/green cutover?

Two-minute defense. “A projection is an optimization with a versioned freshness contract, never authority. I atomically combine inbox identity, expected next version, mutation, and receipt; park gaps; and rebuild into vNext from a complete source with isolated catch-up, control totals, canary cutover, and rollback.”

Event sourcing as an independent command model

Section titled “Event sourcing as an independent command model”

For aggregate A, load the latest compatible snapshot (A, snapshotVersion, state, reducerVersion), read authoritative events after that version, and fold them deterministically. A command validated against version n emits events and performs an optimistic expected-version append requiring the stream tail still equal n; conflict means reload and re-evaluate, not blind retry (C36,C98; F07,F36; A114, retrieved 2026-08-22).

Snapshots are derived acceleration. Snapshot invalidation is mandatory when the event position, reducer, or schema is incompatible. Store the event position and reducer/schema version; invalidate and rebuild a snapshot when an upcaster/reducer change makes it incompatible. Never delete the underlying authoritative sequence merely because a snapshot exists. Use immutable historical payloads plus versioned upcasters into the current in-memory domain shape; for a semantic break, add a new event type and explicit migration rather than reinterpret history (C59,C98; F01,F07).

Inference: temporal queries replay to a recorded position/business time under the event contract; they must distinguish recorded order from effective business time. Run what-if simulations against copied streams/namespaces and label them non- authoritative. Replay isolation uses separate consumers/capacity/checkpoints, deterministic reducers, and external-effect suppression. Historical replay must not resend a transfer, email, market order, or compensation; integrations use an explicit live-mode gate, stable effect ledger/receipt, and reconciliation (C59,C98,C100; F01,F07).

Inference: Kinesis retention alone is not an event store. Kinesis can be one component only if authoritative append concurrency, complete retention, aggregate reads, immutable schema, backup/restore, temporal access, and reconciliation are deliberately supplied (C84,C98; A102,A114, retrieved 2026-08-22).

Inference: DynamoDB Streams alone is not an event store: it is 24-hour CDC over table mutations, and those mutations need not be the authoritative domain-event sequence (C84,C98; A102,A114, retrieved 2026-08-22).

Event-store recovery restores immutable events and metadata to a new namespace, verifies hashes/counts/per-aggregate contiguous versions, reconstructs sampled and then all aggregates, rebuilds projections with effects suppressed, catches up live appends behind a write fence/watermark, reconciles, and cuts over. An event archive is not automatically a financial ledger: ledger postings still need balancing, exact money, reversals/corrections, and reconciliation (C47,C91,C98; F08,F17).

Inference: event sourcing is a poor fit when the team cannot freeze historical meaning, replay deterministically, recover the store, or prevent duplicate external effects; when aggregate histories are unbounded without a safe snapshot policy; or when current-state CRUD plus ledger/outbox already meets audit and temporal requirements (C98; F01,F07).

Problem / why. Event sourcing earns its cost when the authoritative domain must preserve every accepted transition, explain how state arose, reconstruct a point in time, or derive new views from immutable history—not merely because a service already publishes integration events (C36,C98; F01,F07).

Guarantee scope and stopping boundary. Expected-version append serializes one aggregate stream and its event sequence is authoritative for that aggregate. It does not provide cross-aggregate ACID, broker delivery, one-time consumer effects, accounting semantics, or indefinite correctness from a snapshot (C34,C47,C98; F01,F07,F08).

Concrete workload. Evaluate at 1,000 commands/s peak, median 40 events per aggregate, p99 5,000 events, a snapshot evaluation every 500 events, seven-year authoritative retention, and a 60-minute projection-rebuild RTO. These are explicit design-test assumptions, not claims about the repository or an AWS service.

Fit and poor fit. Fit is load-tested reconstruction plus a governed historical/temporal requirement. Poor fit is current-state CRUD with sufficient ledger/audit, mutable event meaning, uncontrolled side effects during replay, unbounded aggregates without compatible snapshots, or no event-store recovery owner (C98; F01,F07).

Failure and recovery. On append conflict, reload and re-evaluate the command; on incompatible snapshot, discard/rebuild it; on corrupt/gapped stream, fence writes, restore events/metadata to a new namespace, verify per-aggregate contiguous versions/hashes, rebuild with effects suppressed, catch up, reconcile, and cut over (C59,C98,C100; F01,F07).

Variable cost. Count immutable append bytes/requests and indexes, snapshot writes/storage, aggregate reads/reconstruction CPU, long retention/archive, upcasting, temporal queries, projection fan-out, replay bandwidth, temporary rebuild capacity, and reconciliation.

Fixed ownership. The domain team owns immutable event meaning, reducers, expected-version behavior, snapshot policy, upcasters/migrations, temporal semantics, effect suppression, store backup/restore, rebuild tooling, audit access, and incident response.

Metric, threshold, response. Measure append conflict ratio, append p99, events/reconstruction time per aggregate, snapshot hit/invalidation, version gaps, upcast failures, replay rate/ETA, rebuild RTO, and reconciliation breaks. At more than 2% conflicts for five minutes, any event gap/upcast failure, or projected 60-minute RTO miss, throttle/fence the affected scope, diagnose writers/schema, restore/replay in isolation, and reconcile before cutover.

Anti-pattern to reject. Calling a retained transport an event store, mutating old event meaning, or replaying history through live payment/notification effects.

Follow-up questions. What is the append concurrency contract? How are old schemas upcast, snapshots invalidated, effects suppressed, and the event store restored within RTO?

Two-minute defense. “Event sourcing changes authority: the per-aggregate event sequence, appended at an expected version, rebuilds state. I adopt it only for an immutable temporal/audit need, with compatible snapshots, upcasters, isolated replay and effects suppression, complete store recovery, metrics, and reconciliation; Kinesis or Streams alone supplies none of that contract.”

A replayable aggregate and a rebuildable projection still do not create one transaction across a bank, venue, and ledger. Sagas address that cross-owner journey. For the example you carry through this section, mark each locally committed step, the first irreversible pivot, and the evidence needed to decide whether a timed-out external step happened.

A saga coordinates local transactions; it does not create cross-service ACID isolation. Compensation is a forward business action, not rollback. A released reservation may fail or conflict; a submitted market order may require cancel, amend, offsetting trade, or human escalation rather than erasure (C39,C101; F05,F10; A116, retrieved 2026-08-22).

DimensionChoreography with EventBridge/SQSOrchestration with Step Functions Standard
Process stateEmerges from participant events plus each service's durable state/inbox/outbox.Explicit workflow state/history, branches, retry/catch, timers, and callbacks.
CouplingProducers know event contracts, not a central step sequence; global dependency graph becomes harder to see.Orchestrator knows sequence and commands; participants need not know one another, but workflow definition becomes a coupling/deployment boundary.
Good fitFew participants, simple reactions, high autonomy, no long opaque chain.Multi-step order/funding/compliance process, durable timeout, human wait, and explicit audit path.
Failure controlsPer-subscription SQS ownership, visibility/DLQ, event ID, timeout events, process-status projection, reconciliation.Idempotent task tokens/commands, effect receipt before transition, retry/catch, wait/callback, timeout, compensation branch, reconciliation.
Residual boundaryEventBridge archive/replay is not original order and SQS/Lambda can repeat.Standard workflow execution semantics do not make downstream effects exactly once.

Documented: Step Functions Standard follows exactly-once workflow execution: tasks and states are not run more than once unless ASL Retry is configured. Boundary: Task invocation, provider completion, and external effects can be ambiguous or non-atomic relative to workflow history. Design response: Use stable identity, idempotency, effect receipts, provider status lookup by business key, compensation, and reconciliation. Asynchronous Express follows at-least-once workflow execution; Synchronous Express follows at-most-once workflow execution (C25,C65; A25, retrieved 2026-08-22). Standard is the long-running, durable/auditable type and supports job-run/callback integrations that Express does not (A25,A26,A87, retrieved 2026-08-22). For example, a task can time out after an external provider commits. Workflow labels do not make arbitrary payments, postings, orders, or notifications one-time effects. This does not make downstream effects exactly once.

A semantic lock exposes the intermediate business state—FUNDS_RESERVED, ORDER_PENDING_COMPLIANCE, CANCEL_REQUESTED—and constrains concurrent actions without pretending database isolation spans the saga. Every lock has owner, expiry/timeout policy, allowed transitions, customer visibility, and repair. The pivot is the step after which normal backward compensation is impossible or undesirable; after broker submission or final posting, prefer forward recovery, explicit reversal/correction, or human resolution. Store causation, attempts, receipts, actor, timestamps, and compensation result in durable audit history.

Inference: use EventBridge/SQS choreography for the repository's simple post-acceptance projection/notification reactions, but Step Functions Standard for explicit multi-step funding/compliance workflows with human wait or deadline state (C60,C62,C63,C65,C101; A84,A85,A116, retrieved 2026-08-22). Neither path replaces service-owned inbox/outbox, idempotent tasks, effect receipts, timeouts, or reconciliation.

Problem / why. A funding/compliance/order journey spans local transaction owners and may wait on providers or humans; a saga makes forward progress, timeouts, and compensating business actions explicit without pretending one distributed ACID transaction exists (C39,C101; F05,F10).

Guarantee scope and stopping boundary. Choreography routes participant facts/commands; orchestration durably records workflow decisions. Neither atomically commits a participant database/provider effect with workflow state, and compensation cannot erase an irreversible pivot (C39,C65,C101; F05,F10).

Concrete workload. Evaluate 200 workflows/s peak, six idempotent local tasks, a five-minute automated-process deadline, human approval waits up to 48 hours with a 24-hour escalation, and 30-day business-receipt retention. These are workflow design assumptions, not current service quotas.

Fit and poor fit. Fit is a long-running cross-owner process with explicit timeouts/receipts and compensable or forward-recoverable steps. Poor fit is one local aggregate transaction, a hard invariant needing synchronous reservation, an undefined compensation, or a high-frequency matching loop.

Failure and recovery. Retry only classified idempotent tasks; after an ambiguous timeout query provider status by business key/receipt; expire semantic locks deliberately; before pivot compensate completed steps where valid; after pivot prefer forward completion, correction/reversal, or human resolution; reconcile process state to participant effects (C39,C45,C101; F05,F10,F11).

Variable cost. Count EventBridge/SQS messages and retries/DLQ storage for choreography, or Standard state transitions/wait history/callbacks for orchestration, plus task compute, receipt/status calls, compensation, audit, and reconciliation (C49,C65; A47,A48,A49, retrieved 2026-08-22).

Fixed ownership. Choreography needs a process owner despite distributed code; orchestration needs a state-machine owner. Both own schemas, task idempotency, semantic locks, pivot/compensation catalog, timeout/human queues, receipts, audit retention, runbooks, reconciliation, security, and on-call.

Metric, threshold, response. Measure workflow age by state, task retry/ timeout/ambiguity, semantic-lock age, compensation rate/failure, callback/human wait age, DLQ age, and receipt mismatches. At five minutes stuck automated age, 24 hours human wait, any failed compensation, or missing monetary receipt, pause new dependent work, query effects, escalate owner/human, compensate or forward-recover, and reconcile (C43,C48,C68,C101; A36, retrieved 2026-08-22).

Anti-pattern to reject. Treating compensation as database rollback or using workflow execution wording as a promise about one-time provider effects.

Follow-up questions. Where is the pivot, which locks can expire, how are ambiguous effects looked up, and who resolves failed compensation or human waits?

Two-minute defense. “I use choreography for a few observable reactions and Standard orchestration when durable process state, timers, or humans matter. Every task carries stable identity and an effect receipt; ambiguous completion is looked up, compensation is a new business action, pivots move recovery forward, and reconciliation—not workflow wording—proves the financial result.”

Hard financial invariants stay authoritative

Section titled “Hard financial invariants stay authoritative”
DecisionMust read/writeWhy projection is insufficient
Available balance and reservationAP12 authoritative cash/security balance, pending reservations, exact currency/scale, and version in one governed transaction/aggregate.Portfolio/cache lag can approve double spend; strong-read projection still has derived provenance.
Risk limitCurrent authoritative exposure plus accepted reservation under the declared risk consistency boundary.Async analytics/search is suitable for surveillance, not pre-trade authorization.
Order transitionAuthoritative order state machine with expected version and stable request/event identity.Arrival order, FIFO, or a status view cannot validate legal transition.
Execution and ledger postingDeterministic fill identity; authoritative execution transition; balanced append-oriented postings and receipts.Event bus/archive and mutable portfolio rows do not provide accounting semantics.

Inference: if one hard invariant cannot fit one aggregate/transaction, name the coordination boundary, reservation/semantic lock, timeout, pivot, compensation limits, and reconciliation. Do not hide it behind “eventual consistency” (C34,C39,C46,C47,C91,C101; F08,F10,F17).

First establish that the projection meets authority, freshness, and recovery requirements. Then test whether its read benefit pays for its additional work and ownership. The dimensionless CU model below teaches the relationship; the later dated USD model uses different inputs and can reach a different economic conclusion. Keep their units and assumptions separate.

The canonical inputs and arithmetic live in the shared cost model. All terms use the same modeled CU; CU is not currency and no mutable AWS price is introduced here. W is million writes/month, R million reads/month, a is CU avoided per million reads, v is transport + projection/index write + storage + replay/rebuild + reconciliation CU per million writes, and F is fixed operational CU/month (C49,C102; F19,F20).

The next table is this chapter's only reader-visible CQRS-model derivation surface. The verifier derives every row from the cost model's canonical input block and compares both documents.

KindKeyVisible value or equationUnit
INPUTwrites_million10million writes/month
INPUTreads_million85million reads/month
INPUTread_work_avoided_cu_per_million1.80CU/million reads
INPUTtransport_cu_per_million_writes0.20CU/million writes
INPUTprojection_write_cu_per_million_writes0.50CU/million writes
INPUTstorage_cu_per_million_writes0.10CU/million writes
INPUTreplay_rebuild_cu_per_million_writes0.08CU/million writes
INPUTreconciliation_cu_per_million_writes0.12CU/million writes
INPUTfixed_operational_cu_per_month18CU/month
INPUTlow_volume_writes_million1million writes/month
INPUThigh_volume_writes_million100million writes/month
INPUThalf_read_benefit_factor0.50ratio
INPUTdouble_projection_write_factor2ratio
INPUTdouble_storage_factor2ratio
FORMULAmonthly_netR * a - W * v - FCU/month
FORMULAbreak_even_ratioR/W = (v + F/W) / areads/write
DERIVATIONvariable_cost_per_million_writes0.20 + 0.50 + 0.10 + 0.08 + 0.12 = 1.00CU/million writes
DERIVATIONread_work_avoided85 * 1.80 = 153.00CU/month
DERIVATIONvariable_projection_cost10 * 1.00 = 10.00CU/month
DERIVATIONfixed_operational_cost18.00CU/month
DERIVATIONnet_benefit153.00 - 10.00 - 18.00 = 125.00CU/month
DERIVATIONbreak_even(1.00 + 18.00 / 10) / 1.80 = 1.56reads/write
DERIVATIONlow_volume_sensitivity(1.00 + 18.00 / 1) / 1.80 = 10.56reads/write
DERIVATIONhigh_volume_sensitivity(1.00 + 18.00 / 100) / 1.80 = 0.66reads/write
DERIVATIONhalf_read_benefit_sensitivity(1.00 + 18.00 / 10) / (1.80 * 0.50) = 3.11reads/write
DERIVATIONdouble_projection_storage_sensitivity0.20 + 0.50 * 2 + 0.10 * 2 + 0.08 + 0.12 = 1.60; (1.60 + 18.00 / 10) / 1.80 = 1.89reads/write
MetricValue
read_work_avoided_cu_month153.0
variable_projection_cu_month10.0
fixed_operational_cu_month18.0
net_benefit_cu_month125.0
break_even_reads_per_write1.56
low_volume_reads_per_write10.56
high_volume_reads_per_write0.66
half_benefit_reads_per_write3.11
double_projection_reads_per_write1.89

Inference: the validated sensitivity rows show why even a positive modeled net benefit does not override an unachievable freshness/recovery contract or a hard authority violation. The CU curve is an architectural sensitivity, not an AWS invoice. The dated cost model puts explicit eu-west-1 prices and fixed operations on a different scale: The dated cost model (source model 10) puts 50 reads/write below its 75.462912 reads/write cost break-even and at -$887.203238/month. At that dated workload, the projection is bought for a measured 95 ms p99 read-latency gain, 2-second freshness, and command/read isolation—not for cost savings. Reprice and remeasure before deciding (A44-A49,A71,A73-A75,A78,A113,A123,A125, retrieved 2026-08-22; C48,C102,C113; F19,F20).

The canonical evidence remains in the repository evidence map. This table applies the patterns without broadening the observations. Authority, ledger, amplification, publication, and saga controls route through C46,C91,C95,C99,C101 and the established accounting/outbox literature F08,F11,F17; CS01–CS12 remain the canonical repository observations.

IDPattern application (model 7)Proof and residual risk
CS01Commit accepted order, durable request result, sentinel, and semantic outbox atomically; AP07 is downstream.Reconcile every accepted order to outbox and intended consumers; relay can repeat.
CS02Receipt includes fingerprint, state, result and accepted version; query overlays it until AP07 catches up.TTL is cleanup; ambiguous attempts need lease/repair evidence.
CS03Persist each relay attempt and inspect every PutEvents entry; retain event ID independent of bus acknowledgement.Producer API acknowledgement is not rule, target, or business completion.
CS04Matcher writes deterministic fill, order transition, execution authority, and semantic outbox inside compatible authoritative boundaries.Cross-account ledger/clearing work still needs inbox/outbox, saga or coordination, and reconciliation.
CS05Duplicate execution returns an effect receipt and independently resumes pending publication.Never let a conditional duplicate short-circuit outbox repair.
CS06AP07 transaction includes inbox tradeId, expected source version, projection mutation, and receipt.Inbox stops identity repeat; version stops stale order; neither upgrades AP07 to authority.
CS07Park future versions, alarm gap age, repair from full source, and reconcile contiguous per-account sequence.EventBridge routing is not causal per-account order.
CS08Give each EventBridge target DLQ the required rule-scoped queue policy and operate failed-DLQ-send separately.A DLQ is holding, not recovery; retain an independent rebuild source.
CS09Replace one-page snapshot Scan with PITR export, manifest, control totals, vNext, catch-up, and cutover.Export is asynchronous and not one business transaction snapshot.
CS10Carry fixed minor units or exact decimals, currency, scale and governed rounding through authority/events/projections.Projection replay must reproduce exact totals; never introduce binary-float intermediates.
CS11Add balanced immutable postings, authoritative reservations/balances, reconciliation batches, and derived portfolio/P&L.Event store, outbox, and saga do not automatically create a ledger.
CS12Keep the matching loop on evidenced long-lived compute/sequencing; use serverless around it.Measure latency, jitter, hot lanes, recovery and cost; no runtime label proves fit.

Choose the simplest step whose evidence answers “yes”:

  1. Is there a measured query SLO/cost/isolation problem that same-store code and indexes cannot solve?
  2. Is authority explicit for every balance, reservation, risk decision, order transition, execution, and posting?
  3. Does each projection specify freshness and overload behavior, order key, read-your-writes, duplicates, rebuild, recovery/reconciliation, and cost?
  4. Can retained sources cover detection + repair + catch-up, with isolated replay capacity and external effects suppressed?
  5. Does the break-even stay positive under lower read benefit, lower write volume, higher fan-out, incidents, and fixed on-call burden?

Stay with one model/store when reads and writes share one shape/SLO, strong current cross-domain reads dominate, volume is too low to amortize operations, or the team cannot safely rebuild. Reject event sourcing when history is not authoritative or replay/evolution cannot be governed. Reject choreography when the process needs visible durable state/human waits. Reject orchestration when a simple local transaction or two participant events are clearer. Reject every design that treats projections as monetary authority or workflow/transport semantics as exactly-once downstream effects.

Conceptual anchors:

Current AWS semantics, all retrieved 2026-08-22:

Choose each pattern for the problem it actually solves: read shape, authoritative history, publication intent, or cross-owner progress. Be ready to explain both the closed boundary and what remains retryable or ambiguous. Carry one projection rebuild and one saga timeout into Reliability, correctness, and recovery, where those residual risks become owned procedures.

Reading layout adapted from SSE reading notes by Mohammed Balila, MIT. Source manifest · Attribution