# CQRS, Event Sourcing, Outbox, and Sagas

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](02-event-driven-foundations.md), the service
boundaries from the [AWS messaging guide](03-aws-messaging-decision-guide.md),
the ordering/replay/backlog limits from the [Kinesis deep dive](04-kinesis-deep-dive.md),
and AP12 ledger/risk authority, AP07 derived portfolio, AP16 semantic outbox,
inbox/version/gap/rebuild, and amplification boundaries from the
[DynamoDB deep dive](05-dynamodb-deep-dive.md). It does not turn a transport,
cache, search index, portfolio, or analytics view into financial authority.

## CQRS is a five-step spectrum

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).

| Step | Separation | State shape | Consistency and recovery | Earn it when | Stop when |
|---:|---|---|---|---|---|
| 1 | Separate code paths | Command 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. |
| 2 | Same-store command/query models | Different 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. |
| 3 | Authoritative store + projection | Commands 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. |
| 4 | Specialized read stores | Separate 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. |
| 5 | Event-sourced command model | The 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.

<!-- TASK7_INFERENCE:complexity_earned -->
**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).

### CQRS spectrum operational contract

**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](#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 and event sourcing are independent

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 authority | Same query model | Separate query model |
|---|---|---|
| Current-state CRUD | Ordinary service, perhaps code-path separation | CQRS with same-store DTOs or asynchronous projections |
| Event sequence | Reconstruct aggregate and answer limited queries directly | Event-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).

<!-- TASK7_INFERENCE:repository_default -->
**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

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).

<!-- TASK7_VARIANTS_BEGIN -->
```text
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
```
<!-- TASK7_VARIANTS_END -->

<!-- TASK7_VARIANTS_RENDER_BEGIN -->
| Variant | Authoritative state | Consistency/freshness lag and overload policy | Ordering scope | Read-your-writes | Duplicate handling | Rebuild method | Recovery/reconciliation | Variable cost | Operational cost | Poor fit | Quantified workload |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 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 |
| 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 |
| 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 |
<!-- TASK7_VARIANTS_RENDER_END -->

## Read-heavy brokerage architecture

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).

```mermaid
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
```

<!-- TASK7_INFERENCE:write_amplification -->
**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.

<!-- TASK7_INFERENCE:read_your_writes -->
**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).

<!-- TASK7_INFERENCE:read_heavy_residual_risks -->
**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).

## Write-heavy execution-stream architecture

Use the reviewed Task 5 workload: `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).

```mermaid
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).

<!-- TASK7_INFERENCE:matching_capacity -->
**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).

<!-- TASK7_INFERENCE:write_heavy_kinesis_choice -->
**Inference:** Kinesis is the default buffer here because Task 5 measured that
contract. 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).

<!-- TASK7_INFERENCE:status_lag -->
**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).

## Mixed workload: repository CQRS-lite

The default keeps the reviewed Task 6 transaction: 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).

```mermaid
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 Task 6's `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.

<!-- TASK7_INFERENCE:specialized_store -->
**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

| Variant | Atomicity gap closed | What remains |
|---|---|---|
| DynamoDB semantic outbox item + Streams relay | `TransactWriteItems` 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 CDC | It 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/CDC | One 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 write | No 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). |

<!-- TASK7_INFERENCE:outbox_retention -->
**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).

### Outbox and CDC operational contract

**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

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.

<!-- TASK7_INFERENCE:projection_versions -->
**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).

<!-- TASK7_INFERENCE:blue_green_rebuild -->
**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

**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

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).

<!-- TASK7_INFERENCE:temporal_queries -->
**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).

<!-- TASK7_INFERENCE:kinesis_not_event_store -->
**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).

<!-- TASK7_INFERENCE:streams_not_event_store -->
**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).

<!-- TASK7_INFERENCE:event_sourcing_poor_fit -->
**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).

### Event-sourcing operational contract

**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.”

## Sagas: choreography versus orchestration

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).

| Dimension | Choreography with EventBridge/SQS | Orchestration with Step Functions Standard |
|---|---|---|
| Process state | Emerges from participant events plus each service's durable state/inbox/outbox. | Explicit workflow state/history, branches, retry/catch, timers, and callbacks. |
| Coupling | Producers 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 fit | Few participants, simple reactions, high autonomy, no long opaque chain. | Multi-step order/funding/compliance process, durable timeout, human wait, and explicit audit path. |
| Failure controls | Per-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 boundary | EventBridge 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.

<!-- TASK7_INFERENCE:saga_recommendation -->
**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.

### Saga operational contract

**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

| Decision | Must read/write | Why projection is insufficient |
|---|---|---|
| Available balance and reservation | AP12 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 limit | Current authoritative exposure plus accepted reservation under the declared risk consistency boundary. | Async analytics/search is suitable for surveillance, not pre-trade authorization. |
| Order transition | Authoritative 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 posting | Deterministic fill identity; authoritative execution transition; balanced append-oriented postings and receipts. | Event bus/archive and mutable portfolio rows do not provide accounting semantics. |

<!-- TASK7_INFERENCE:hard_invariant_inference -->
**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).

## CQRS break-even model

The canonical inputs and arithmetic live in
[the shared cost model](../research/cost-model.md#cqrs-break-even-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.

<!-- TASK7_CHAPTER_CQRS_VISIBLE_BEGIN -->
| Kind | Key | Visible value or equation | Unit |
|---|---|---|---|
| INPUT | writes_million | 10 | million writes/month |
| INPUT | reads_million | 85 | million reads/month |
| INPUT | read_work_avoided_cu_per_million | 1.80 | CU/million reads |
| INPUT | transport_cu_per_million_writes | 0.20 | CU/million writes |
| INPUT | projection_write_cu_per_million_writes | 0.50 | CU/million writes |
| INPUT | storage_cu_per_million_writes | 0.10 | CU/million writes |
| INPUT | replay_rebuild_cu_per_million_writes | 0.08 | CU/million writes |
| INPUT | reconciliation_cu_per_million_writes | 0.12 | CU/million writes |
| INPUT | fixed_operational_cu_per_month | 18 | CU/month |
| INPUT | low_volume_writes_million | 1 | million writes/month |
| INPUT | high_volume_writes_million | 100 | million writes/month |
| INPUT | half_read_benefit_factor | 0.50 | ratio |
| INPUT | double_projection_write_factor | 2 | ratio |
| INPUT | double_storage_factor | 2 | ratio |
| FORMULA | monthly_net | R * a - W * v - F | CU/month |
| FORMULA | break_even_ratio | R/W = (v + F/W) / a | reads/write |
| DERIVATION | variable_cost_per_million_writes | 0.20 + 0.50 + 0.10 + 0.08 + 0.12 = 1.00 | CU/million writes |
| DERIVATION | read_work_avoided | 85 * 1.80 = 153.00 | CU/month |
| DERIVATION | variable_projection_cost | 10 * 1.00 = 10.00 | CU/month |
| DERIVATION | fixed_operational_cost | 18.00 | CU/month |
| DERIVATION | net_benefit | 153.00 - 10.00 - 18.00 = 125.00 | CU/month |
| DERIVATION | break_even | (1.00 + 18.00 / 10) / 1.80 = 1.56 | reads/write |
| DERIVATION | low_volume_sensitivity | (1.00 + 18.00 / 1) / 1.80 = 10.56 | reads/write |
| DERIVATION | high_volume_sensitivity | (1.00 + 18.00 / 100) / 1.80 = 0.66 | reads/write |
| DERIVATION | half_read_benefit_sensitivity | (1.00 + 18.00 / 10) / (1.80 * 0.50) = 3.11 | reads/write |
| DERIVATION | double_projection_storage_sensitivity | 0.20 + 0.50 * 2 + 0.10 * 2 + 0.08 + 0.12 = 1.60; (1.60 + 18.00 / 10) / 1.80 = 1.89 | reads/write |
<!-- TASK7_CHAPTER_CQRS_VISIBLE_END -->

<!-- TASK7_CHAPTER_CQRS_RENDER_BEGIN -->
| Metric | Value |
|---|---:|
| read_work_avoided_cu_month | 153.0 |
| variable_projection_cu_month | 10.0 |
| fixed_operational_cu_month | 18.0 |
| net_benefit_cu_month | 125.0 |
| break_even_reads_per_write | 1.56 |
| low_volume_reads_per_write | 10.56 |
| high_volume_reads_per_write | 0.66 |
| half_benefit_reads_per_write | 3.11 |
| double_projection_reads_per_write | 1.89 |
<!-- TASK7_CHAPTER_CQRS_RENDER_END -->

<!-- TASK7_INFERENCE:break_even_inference -->
**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](09-cost-performance-and-capacity.md#cqrs-dated-break-even)
puts explicit `eu-west-1` prices and fixed operations on a different scale:
Task 10's dated model 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).

## Repository application: CS01–CS12

The canonical evidence remains in the
[repository evidence map](../research/repository-evidence.md#twelve-required-issue-routes).
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.

| ID | Task 7 application | Proof and residual risk |
|---|---|---|
| CS01 | Commit 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. |
| CS02 | Receipt includes fingerprint, state, result and accepted version; query overlays it until AP07 catches up. | TTL is cleanup; ambiguous attempts need lease/repair evidence. |
| CS03 | Persist 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. |
| CS04 | Matcher 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. |
| CS05 | Duplicate execution returns an effect receipt and independently resumes pending publication. | Never let a conditional duplicate short-circuit outbox repair. |
| CS06 | AP07 transaction includes inbox `tradeId`, expected source version, projection mutation, and receipt. | Inbox stops identity repeat; version stops stale order; neither upgrades AP07 to authority. |
| CS07 | Park future versions, alarm gap age, repair from full source, and reconcile contiguous per-account sequence. | EventBridge routing is not causal per-account order. |
| CS08 | Give 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. |
| CS09 | Replace one-page snapshot Scan with PITR export, manifest, control totals, `vNext`, catch-up, and cutover. | Export is asynchronous and not one business transaction snapshot. |
| CS10 | Carry 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. |
| CS11 | Add balanced immutable postings, authoritative reservations/balances, reconciliation batches, and derived portfolio/P&L. | Event store, outbox, and saga do not automatically create a ledger. |
| CS12 | Keep 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. |

## Decision and poor-fit checklist

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.

## Sources used in this chapter

Conceptual anchors:

- Martin Fowler, [CQRS](https://martinfowler.com/bliki/CQRS.html) and
  [Event Sourcing](https://martinfowler.com/eaaDev/EventSourcing.html)
  (F06,F07; C35,C36,C97,C98).
- Rinat Abdullin (compiler/PDF metadata author),
  [archived compilation of material attributed to Greg Young](https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf)
  (F36; supporting cross-reference; no verified author-controlled original
  locator; C97,C98). Fowler F06/F07 remain the controlling conceptual anchors.
- Chris Richardson, [Transactional Outbox](https://microservices.io/patterns/data/transactional-outbox.html)
  and [Saga](https://microservices.io/patterns/data/saga.html) (F04,F05;
  C38,C39,C99,C101).
- Garcia-Molina and Salem, [Sagas](https://doi.org/10.1145/38713.38742)
  (F10; C39,C65,C101); Hohpe and Woolf,
  [*Enterprise Integration Patterns*](https://www.enterpriseintegrationpatterns.com/)
  (F02; C40,C42,C43,C100); Kleppmann and Riccomini,
  [*Designing Data-Intensive Applications*](https://www.oreilly.com/library/view/designing-data-intensive-applications/9781098119058/)
  (F01; C34,C41,C59,C98,C100).
- Fowler, [Accounting Patterns](https://martinfowler.com/eaaDev/AccountingNarrative.html)
  (F08); Featonby, [Making retries safe](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/)
  (F11); Yanacek, [Avoiding insurmountable queue backlogs](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs/)
  (F13); Google [SRE](https://sre.google/sre-book/table-of-contents/) (F14);
  FinOps [Unit Economics](https://www.finops.org/framework/capabilities/unit-economics/)
  (F19); Vogels, [The Frugal Architect](https://thefrugalarchitect.com/) (F20);
  DeBrie, [The DynamoDB Book](https://www.dynamodbbook.com/) and
  [single-table design](https://www.alexdebrie.com/posts/dynamodb-single-table/)
  (F21,F22).

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

- AWS Prescriptive Guidance: [CQRS](https://docs.aws.amazon.com/prescriptive-guidance/latest/modernization-data-persistence/cqrs-pattern.html)
  (A113), [event sourcing](https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/event-sourcing-pattern.html)
  (A114), [transactional outbox](https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/transactional-outbox.html)
  (A115), and [sagas](https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/saga-patterns.html)
  (A116; retrieved 2026-08-22).
- DynamoDB [consistency](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html)
  (A19), [transactions](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transactions.html)
  (A20,A101), [Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html)
  (A21,A102), and [export/recovery](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/S3DataExport.HowItWorks.html)
  (A106; retrieved 2026-08-22).
- Step Functions [workflow types](https://docs.aws.amazon.com/step-functions/latest/dg/choosing-workflow-type.html),
  [errors](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-error-handling.html),
  and [integration patterns](https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html)
  (A25,A26,A87).
- EventBridge [archive/replay](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-archive.html)
  (A84); SQS [visibility](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html)
  (A85); OpenSearch [service](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/what-is.html)
  (A32); ElastiCache [service](https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/WhatIs.html)
  (A34); Aurora [replication](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Replication.html)
  (A35).
- Cost routes: [Lambda](https://aws.amazon.com/lambda/pricing/) (A44),
  [DynamoDB](https://aws.amazon.com/dynamodb/pricing/) (A45),
  [Kinesis](https://aws.amazon.com/kinesis/data-streams/pricing/) (A46),
  [EventBridge](https://aws.amazon.com/eventbridge/pricing/) (A47),
  [SQS/SNS](https://aws.amazon.com/sqs/pricing/) (A48),
  [Step Functions](https://aws.amazon.com/step-functions/pricing/) (A49),
  [S3](https://aws.amazon.com/s3/pricing/) (A75), and
  [OpenSearch](https://aws.amazon.com/opensearch-service/pricing/) (A78).
