[Docs] ADR-0019: commerce aggregates are one storage document each, and the SQL guards they replace - #254
Merged
vedanshujain merged 1 commit intoSep 13, 2026
Conversation
…nd the SQL guards they replace Records the document model the commerce adapters are built on: an invariant spanning two facts lives in ONE document; a coupling spanning two aggregates is made idempotently completable by any replayer and swept. `updateIf` for contended pure-counter writes, `compareAndSet` with bounded jittered retry for everything multi-field. Inventory is described as built, not as planned: a durable per-key claim, a reverse index written before the hold, then the guarded decrement and the hold in one compare-and-set — a two-step with one healed crash window and one mitigated one, plus two early out-of-stock exits that differ in whether they consume the key. The terminal-answer-before-prune ordering rule, the bounded movement ring with its accepted residual and the sweeper contract that closes it, and the contention budget are recorded with the numbers the code asserts, distinguished from the numbers that are only measured and logged. Five couplings needed a ruling because the naive translation is wrong, and each is recorded as design with its owning increment: coupon redemption inverts the claim order because there is no transaction to roll a refused bump back; the email lease and the orders customer filter each denormalise an OR into one indexed field; live-sku uniqueness across products and variants becomes a claim document, replacing two partial unique indexes; refunds keep a four-state capacity lifecycle arbitrated only on the reserve path. Because the Kysely stores are deleted later in this effort, section 7 snapshots the guard semantics of every statement the design replaces — predicate, the invariant it holds, the document write that now holds it, and the suite that proves it, saying so where a suite must still be written. Places where the plan disagreed with the code are corrected in favour of the code, including the refund row lock (a real timestamp write, not `FOR UPDATE` and not a self-assignment), the outbox write (a do-nothing conflict, not an upsert), the batch methods (two take id sets, not four), the lost finalizer (it echoes the winner rather than rolling back work), and order creation (only the items insert is multi-row). Two hazards are named rather than inherited silently: the login throttle has no unique constraint to fall back on, and cross-customer address isolation must become an explicit ownership check. ADR-0013, 0016 and 0017 are unchanged and respected; nothing is amended. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011NjdC8awspUte5wML6eY2X
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
ADR-0019 records the document model the commerce adapters implement on plugin storage: the
inventory model as actually built, the design the remaining adapters must satisfy, and — because
the Postgres adapter is deleted later in this work order — a prose snapshot of every SQL guard
that model replaces, each mapped to the document write that now carries the invariant and the
suite that proves it. This is the ninth increment of work order 02. Documentation only.
What the record decides
atomicity rather than bookkeeping, the bounded ring for movements, the one accepted residual,
and the sweeper contract that reaps it.
search, a denormalised customer key served through an
inpredicate, a single due-at field forthe email lease, per-customer coupon claims with compensation instead of a unique constraint, a
live-sku claim document, and the refund capacity lifecycle folded into one CAS.
line between what is asserted in a test and what is only measured.
The snapshot
Section 7 is a 17-subsection prose snapshot (7.1–7.17) covering inventory, orders, carts, coupons,
product commerce, settings, entitlements, addresses, sessions, credentials, shipping and tax
rules, notes, payment events and reporting. Every quoted predicate was machine-checked against the
current Postgres adapter — 113 checks, 0 failures — and every number from the built model (section
2) was checked against the storage adapter's own source — 48 checks, 0 failures. Obligations for
adapters not yet built are written in future tense, each tagged with its owning increment. Two
known gaps are recorded as follow-ups outside this work order: the port docblock does not yet
document the retryable contention outcome, and the in-memory inventory fake disagrees with the
port on null-expiry adoption.
Deliberate staleness
ADR-0018's body still says ADR-0019 is "to be written" — left as-is under the folder's
amend-don't-rewrite rule; the README index is corrected instead.
Length
826 lines, justified by the census and full-snapshot scope this record covers — it is written to
survive the Postgres adapter's deletion later in this work order.
Verification
Lint, typecheck and format all clean. All 13 tables in the document render with consistent column
counts. Hygiene grep over the diff is clean. All markdown links resolve. The
adr/README.mddiffis limited to the new 0019 entry, the 0018 correction, and a Queued-list annotation.
Review
Two independent reviews over four rounds; the first three requested changes on snapshot
completeness, factual characterisation of the SQL, and tense. Both approve. An independent check
re-verified sampled citations against the current adapter.
🤖 Generated with Claude Code
https://claude.ai/code/session_011NjdC8awspUte5wML6eY2X