AdvancedTransactionsInventoryIdempotencySearchOutbox

E-commerce Platform

Catalog, inventory, checkout, fulfillment, and returns without overselling.

Product overview

Sell physical goods from a searchable catalog while keeping prices, stock, money, fulfillment, and returns correct across retries and partial failures.

Main actors

Customer, Merchant, Warehouse operator, Support.

Assumptions

  • One legal seller of record in v0.1
  • Inventory is stocked at one or more fulfillment locations
  • A PCI-compliant provider hosts card collection
  • Example workload: 25 orders/second at peak, not a benchmark

Non-goals

  • Marketplace seller onboarding
  • Warehouse robotics
  • Building a payment processor
  • Globally active-active checkout

Requirements

Must-have

  • Browse and search purchasable products
  • Maintain server-authoritative carts
  • Reserve inventory during checkout
  • Authorize payment and create orders idempotently
  • Track shipments and process refunds

Should-have

  • Guest checkout
  • Promotion rules with an explainable price breakdown
  • Split fulfillment by location
  • Customer service order timeline

Future scope

  • Marketplace sellers
  • Subscriptions
  • Cross-border duties
  • Personalized ranking

Non-functional decisions

ConcernDecision
AvailabilityBrowsing may degrade to cached catalog data; checkout fails closed if price, inventory, or payment dependencies are uncertain.
ConsistencyOrder totals and stock reservations are transactional. Search, recommendations, and shipment tracking may lag.
DurabilityAcknowledged orders and outbox records share one database commit; payment state is reconciled with the provider.
PrivacyMinimize stored addresses, encrypt sensitive fields, and apply retention to guest data.
LatencyKeep catalog reads cacheable; checkout is slower but bounded by provider timeouts and explicit pending states.

User roles

RoleAccess boundary
CustomerOwn carts, orders, addresses, and return requests
Catalog managerProducts and prices; no payment or support impersonation
Warehouse operatorAssigned fulfillment tasks and stock adjustments
SupportScoped order view and audited refund request, never raw payment credentials

Core workflows

Checkout
Load a server-priced cart → Validate purchasability and shipping address → Reserve stock in a transaction → Create pending order → Authorize provider payment → Confirm order and publish through an outbox → Release reservation on failure
Fulfillment
Allocate order lines to a location → Pick and pack → Purchase carrier label → Mark shipment dispatched → Consume reservation → Notify customer
Return
Check return window and line eligibility → Issue return authorization → Receive and inspect item → Restock or quarantine → Request refund → Close return after webhook confirmation

Domain model

EntitiesRelationship
Product → VariantA product presents one or more sellable SKUs
Cart → CartLineA cart snapshots quantity, not authoritative price
Order → OrderLineImmutable commercial snapshot of SKU, price, tax, and discount
InventoryItem → ReservationOn-hand stock is protected by expiring reservations
Order → Shipment / ReturnAn order can split into shipments and later line-level returns

Database design

PostgreSQL remains the transactional source of truth. Add specialized stores only for the workloads called out below.

product_variants — Sellable SKU and catalog state

product_variants ---------------- id UUID PK product_id UUID FK products sku TEXT status TEXT price_minor BIGINT currency CHAR(3) version INT Constraints: UNIQUE(sku); price_minor >= 0 Indexes: (product_id); (status, updated_at) for indexing feed

inventory — Stock by SKU and location

inventory --------- variant_id UUID FK location_id UUID FK on_hand INT reserved INT version INT Constraints: PK(variant_id, location_id); reserved BETWEEN 0 AND on_hand Indexes: (location_id, variant_id) supports allocation

orders — Checkout aggregate and payment state

orders ------ id UUID PK customer_id UUID FK NULL status TEXT currency CHAR(3) total_minor BIGINT payment_intent_ref TEXT created_at TIMESTAMPTZ Constraints: UNIQUE(payment_intent_ref); total_minor >= 0 Indexes: (customer_id, created_at DESC); (status, created_at) for operations

order_lines — Immutable purchase snapshot

order_lines ----------- id UUID PK order_id UUID FK variant_id UUID FK sku TEXT quantity INT unit_price_minor BIGINT tax_minor BIGINT Constraints: quantity > 0; UNIQUE(order_id, id) Indexes: (order_id)

inventory_reservations — Expiring claim on stock

inventory_reservations ---------------------- id UUID PK order_id UUID FK variant_id UUID FK location_id UUID FK quantity INT expires_at TIMESTAMPTZ status TEXT Constraints: UNIQUE(order_id, variant_id, location_id) Indexes: (status, expires_at) for reaper

Transaction and retention boundaries

  • Lock relevant inventory rows in deterministic SKU order, verify available = on_hand - reserved, increment reserved, insert reservations, order, and outbox atomically
  • Refund records and order refundable amount update share a transaction; provider calls happen outside it
  • Hard-delete abandoned carts after retention; never soft-delete commercial order records—use explicit states

API design

GET /v1/products?cursor=… POST /v1/carts/:id/lines POST /v1/orders GET /v1/orders/:id POST /v1/orders/:id/cancel POST /v1/returns
EndpointPurpose and permissionIdempotency / status
GET /v1/products?cursor=…Browse catalog using opaque stable cursor
Public
Not required
200, 400
POST /v1/carts/:id/linesAdd or replace a cart line
Cart owner
Not required
200, 409
POST /v1/ordersPrice cart, reserve inventory, and start checkout
Cart owner
Required; key scoped to account and payload hash
201, 402, 409, 422
GET /v1/orders/:idRead order timeline
Order owner or scoped support role
Not required
200, 403, 404
POST /v1/orders/:id/cancelRequest state-aware cancellation
Owner or support
Required
202, 409
POST /v1/returnsCreate return authorization for eligible lines
Order owner or support
Required
201, 409, 422

List endpoints use opaque keyset cursors unless a small bounded collection is explicit. Unknown objects return 404 where 403 would leak existence.

Architecture

System context

E-commerce Platform system context
E-commerce Platform system context

Main application architecture

E-commerce Platform application architecture
E-commerce Platform application architecture

Critical workflow sequence

E-commerce Platform workflow sequence
E-commerce Platform workflow sequence

Event model

EventProducer → consumersDelivery
OrderConfirmedCheckout → Fulfillment, email, analyticsTransactional outbox; at-least-once; consumers deduplicate by event id
ReservationExpiredInventory worker → Order workflowRetry safely; conditional status transition prevents releasing twice
ShipmentDispatchedFulfillment → Notifications, order projectionDead-letter after bounded retries; operations can replay

Failure modes

Stock race

Failure
Two checkouts see the last item.

Mitigation
Row lock and invariant check inside one reservation transaction; loser receives 409.

Payment timeout

Failure
Provider authorizes but the API sees a timeout.

Mitigation
Keep order payment_pending; query by provider idempotency reference and reconcile before retrying.

Duplicate submit

Failure
Client retries POST /orders.

Mitigation
Persist idempotency key, payload hash, status, and prior response under the same account.

Event redelivery

Failure
Fulfillment receives OrderConfirmed twice.

Mitigation
Inbox/event receipt unique constraint and idempotent task creation.

Reservation leak

Failure
Worker crashes after payment failure.

Mitigation
Expiry-indexed reaper releases only active reservations with conditional updates.

Stale catalog

Failure
Cached page shows an old price.

Mitigation
Display indicative price; reprice server-side and require explicit confirmation when total changes.

Consistency and concurrency

Strong consistency / transactions

  • Inventory reservation
  • Order total snapshot
  • Refundable balance
  • State transition preconditions

Eventual convergence

  • Search index
  • Recommendations
  • Email
  • Carrier tracking projection

Concurrency controls

  • Use SELECT … FOR UPDATE on inventory rows, not a distributed lock
  • A unique idempotency record prevents duplicate orders
  • Order version or conditional status UPDATE rejects cancellation after dispatch
  • Refund sum is checked while locking the order payment row

Caching and background work

Safe to cache

  • Published catalog by locale
  • Product media
  • Facets and popular search results

Never a cache authority

  • Available-to-promise during checkout
  • Authoritative price at order creation
  • Refundable amount

Cache-aside with short TTL plus product-change invalidation; CDN assets use content hashes. Serve stale catalog on read failure but never bypass checkout validation.

Background jobs

  • Expire stock reservations
  • Publish transactional outbox
  • Index product changes
  • Send receipts
  • Poll carrier status
  • Reconcile payment intents and refunds

Security boundaries

Security claims here are design controls, not certification. Threat modeling and jurisdiction-specific review remain required.

  • Object-level checks on every cart, order, address, and return
  • Hosted payment fields keep card data outside the application boundary
  • Signed provider webhooks with timestamp tolerance and replay deduplication
  • Promotion and quantity inputs revalidated server-side
  • Rate limits by account, IP, and checkout risk signals
  • Audit privileged refunds and inventory adjustments with reason
  • Prevent stored XSS in merchant-authored catalog content

Observability

Logs

  • Order, reservation, payment, and fulfillment transitions with request/order ids and reason—never addresses or payment tokens
  • Checkout repricing differences and rejected stock invariants
  • Outbox/worker attempt with event id and terminal outcome

Metrics

  • Checkout authorization failures and payment_pending age
  • Inventory reservation conflict and expiry rate by SKU/location
  • Order-to-allocation and allocation-to-dispatch latency
  • Outbox age, fulfillment backlog, and refund reconciliation exceptions

Traces

  • Trace checkout across pricing, inventory transaction, provider authorization, outbox, and fulfillment
  • Trace return receipt through refund initiation and provider convergence

Alerts

  • Confirmed orders without fulfillment work
  • Payment-pending orders exceed reconciliation window
  • Reserved exceeds on-hand invariant or reservation expiry worker stalls

Testing strategy

LayerWhat it proves
UnitMoney arithmetic, promotion precedence, state machines, reservation expiry
IntegrationReal PostgreSQL constraints, row locking, outbox atomicity, provider adapter sandbox
ContractPayment and carrier webhook fixtures, consumer event schemas
ConcurrencyMany checkouts for one SKU; assert reserved never exceeds on_hand
FailureTimeout after authorization, duplicate events, worker death before acknowledgement
End-to-end/loadBrowse → checkout → cancel/ship/return; workload shaped by browse-to-buy ratio

Do not mock PostgreSQL constraints, transaction isolation, object-store semantics, or provider contract fixtures in the tests intended to prove those boundaries.

Deployment evolution

Stage 1
Next.js UI/API, PostgreSQL, one worker, provider-hosted checkout, object storage
Stage 2
Load balancer, API replicas, Redis catalog cache, managed queue, search engine, PostgreSQL primary/read replica
Stage 3
Separate catalog/search from transactional ordering; partition inventory by region only after measured contention

Scale evolution

These are architecture scenarios, not throughput claims. Each transition should follow measured workload and reliability pressure.

ScenarioArchitecture may evolve towardDecision rule
1K usersOne API and worker, PostgreSQL for orders and stock, managed object storage for product imagesKeep stock reservation and order creation in one PostgreSQL transaction; measure reservation lock waits before adding infrastructure
100K usersAPI replicas; Redis for catalog; search service; worker pools; database read replicas while writes remain primary-ownedAdd Redis and search when catalog reads dominate; replicas never answer stock questions during checkout
1M+ usersRegional catalog edges and partitioned order/stock ownership; separate fulfillment and search workloads; never multi-writer inventory without an explicit conflict modelSplit stock ownership by warehouse or region only after hot-SKU lock contention is measured; each SKU keeps one writer