Design an Always-Writable Shopping Cart (Amazon)
The Amazon shopping cart is the foundational real-world use case that inspired Werner Vogels and the Amazon engineering team to develop the Dynamo storage architecture in 2007. In e-commerce, rejecting a customer's request to add an item to their cart translates directly into lost revenue. Consequently, the shopping cart is engineered as an "Always-Writable" distributed system, prioritizing extreme write availability and partition tolerance (AP in the CAP theorem) over strict immediate consistency.
1. Understanding the Problem
Functional Requirements
- Cart Modifications: Add, update quantity, and remove items from a cart.
- Guest vs Authenticated User Persistence: Support anonymous guest shopping carts (via session cookies) and seamlessly merge guest carts into authenticated user accounts upon login.
- Cart Viewing: Retrieve current cart contents with item details, prices, and discounts in .
- Cross-Device Sync: A user adding an item on their mobile phone must see it appear on their laptop cart.
- Checkout Handoff: Transition cart contents into the Order and Inventory Reservation service during checkout.
Non-Functional Requirements
- Always-Writable (100% Write Availability): An
Add to Cartrequest must never fail, even during cross-region network partitions, node crashes, or database degraded modes. - Ultra-Low Latency: P99 write latency ; P99 read latency .
- Eventual Consistency: Replicas converge deterministically. If concurrent divergent writes occur, the system must never drop an item the customer intended to purchase.
- Scale: Support 300+ Million active user carts handling 50,000+ peak write QPS.
Capacity Estimations & Sizing (5 Years)
- Active Carts: 300 Million active shopping carts.
- Cart Size: Average cart contains 5 items.
- Average Record Size:
user_id(16 bytes) + 5 items (item_id: 16B, qty: 4B, price_snapshot: 8B, timestamp: 8B) 250 bytes. - Storage Sizing: With replication across regions: . Insight: The entire global shopping cart dataset easily fits into the memory of a distributed Redis / DynamoDB cluster!
- Throughput Sizing:
- Daily Cart Operations: 500 Million actions/day.
- Average QPS: .
- Peak Flash-Sale / Prime Day QPS: 50,000+ QPS.
2. The Set Up
Defining Core Entities
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SHOPPING_CART β
ββββββββββββββββββββ¬βββββββββββββββ¬βββββββββββββββββββββββ€
β cart_id β VARCHAR(64) β PK (user_id / anon) β
β owner_type β ENUM β AUTHENTICATED, GUEST β
β items_json β JSONB β Map of Item Elements β
β vector_clock β VARCHAR(128) β Version Causality β
β updated_at β TIMESTAMP β Last Modified Time β
β ttl_seconds β INT β Expire after 30 days β
ββββββββββββββββββββ΄βββββββββββββββ΄βββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CART_ITEM β
ββββββββββββββββββββ¬βββββββββββββββ¬βββββββββββββββββββββββ€
β sku_id β VARCHAR(32) β Product Identifier β
β quantity β INT β Item Count β
β price_cents β INT β Price at Add Time β
β added_timestamp β BIGINT β Unix Microsecond β
β is_deleted β BOOLEAN β Tombstone Flag β
ββββββββββββββββββββ΄βββββββββββββββ΄βββββββββββββββββββββββ
The API Design
1. Add / Update Item in Cartβ
POST /api/v1/cart/items
Content-Type: application/json
Authorization: Bearer <user_jwt_or_guest_cookie>
X-Vector-Clock: "nodeA:4;nodeB:2"
{
"sku_id": "SKU-9481-B",
"quantity_delta": 1,
"unit_price_cents": 2999
}
Response (200 OK):
{
"cart_id": "user_94812",
"total_items": 4,
"new_vector_clock": "nodeA:5;nodeB:2",
"items": [
{"sku_id": "SKU-9481-B", "quantity": 2, "unit_price_cents": 2999}
]
}
2. Merge Guest Cart into User Account (Upon Login)β
POST /api/v1/cart/merge
Content-Type: application/json
Authorization: Bearer <user_authenticated_token>
{
"guest_session_token": "guest_sess_7a81094b"
}
Response (200 OK):
{
"cart_id": "user_94812",
"merged_items_count": 5,
"status": "CONVERGED"
}
3. High-Level Design
Walkthrough of Core Flows
1. The Always-Writable Write Pathβ
- The user clicks "Add to Cart".
- The request hits the Cart Microservice behind the API Gateway.
- The service writes to an AP-mode Distributed Key-Value Store (DynamoDB / Cassandra):
- Configured with write quorum (or sloppy quorum with hinted handoff).
- The coordinator node writes to the first reachable healthy replica.
- Even if 2 out of 3 replicas are partitioned or down, the write succeeds immediately in !
- The response includes an updated Vector Clock header back to the client.
2. The Read Path & Conflict Reconciliation (Read Repair)β
- User navigates to
/cart. - The service queries replicas on the consistent hash ring.
- If both replicas return the exact same vector clock, the cart is returned immediately.
- Divergence Detected (Siblings):
- If two replicas return diverging vector clocks (caused by concurrent writes during a network split), the Cart Service executes Domain-Specific Conflict Resolution:
- The "Add-Wins" Business Rule: The cart merges the items using a set union. If Replica 1 has 2 books and Replica 2 has 1 pair of shoes, the merged cart contains both 2 books and 1 pair of shoes!
- The resolved cart is returned to the user and asynchronously written back to the replicas (Read Repair).
4. Potential Deep Dives & Bottlenecks
Deep Dive 1: Why Amazon Chose Availability Over Consistency (The Dynamo Philosophy)
What happens if the shopping cart uses a traditional CP relational database (like PostgreSQL with synchronous 2-phase commit)?
- During a network partition between US-East and US-West datacenters, a CP system blocks writes to preserve consistency:
- The Financial Impact: Customers who receive error screens frequently abandon their shopping sessions entirely.
- The AP Trade-Off: Vogels famously established that it is far better to occasionally resurrect a deleted item in a shopping cart (which the user can easily remove before paying) than to prevent the user from adding an item in the first place!
Deep Dive 2: Guest Cart to Authenticated User Cart Merging
What happens when an anonymous user browses for 30 minutes, adds 3 items to a guest cart, and then logs in?
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GUEST-TO-AUTH CART MERGE PIPELINE β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Guest Cart (Cookie ID: guest_8410): β
β β’ Item A (Qty: 1) β
β β’ Item B (Qty: 2) β
β β
β Existing User Cart (User ID: user_9912): β
β β’ Item B (Qty: 1) β
β β’ Item C (Qty: 1) β
β β
β AUTHENTICATION EVENT β
β β β
β βΌ β
β Merged Result Cart (User ID: user_9912): β
β β’ Item A: Qty = 1 β
β β’ Item B: Qty = MAX(2, 1) = 2 (or 2+1=3 based on rule)β
β β’ Item C: Qty = 1 β
β β
β Action: Delete guest_8410 record asynchronously. β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Business Rule Decision: For duplicate items across guest and user carts:
- Default: Take (e.g. user was likely adding the same item they saw earlier).
- Alternative: Sum quantities () with a safety cap against warehouse inventory limits.
Deep Dive 3: Conflict-Free Replicated Data Types (CRDTs: PN-Counter)
How can we mathematically guarantee convergence without complex manual sibling merging?
- Use a PN-Counter (Positive-Negative Counter) CRDT for each item:
- : monotonically increasing counter for additions.
- : monotonically increasing counter for subtractions/deletions.
- Current Quantity: .
- Merge Operation ():
- Because is commutative, associative, and idempotent, any replica can merge divergent state in any order and arrive at the exact same deterministic quantity!
Deep Dive 4: Price Snapshotting vs Live Inventory Validation at Checkout
Does adding an item to the shopping cart reserve warehouse stock?
- No! Holding warehouse inventory for every cart addition is an anti-pattern. Millions of users abandon carts; reserving inventory would lock up items that active buyers want to purchase.
- The Two-Stage Checkout Transition:
- Shopping Cart Phase: Cart holds loose item references with a timestamped price snapshot. Stock is not reserved.
- Checkout Phase: When the user clicks "Proceed to Checkout":
- The system locks the cart.
- Calls the Pricing Service to verify current prices (informing the user if a price changed).
- Calls the Inventory Service to execute an atomic reservation with a 10-minute hold TTL (via Redis Lua /
SKIP LOCKED).
5. Architectural Trade-Off Matrix
| Design Alternative | Option A | Option B | Selected Choice & Rationale |
|---|---|---|---|
| CAP Classification | CP (Strict ACID PostgreSQL) | AP (Dynamo-style Eventual Consistency) | AP (Eventual Consistency): Guarantees 100% write availability; "Add to Cart" never drops a paying customer. |
| Conflict Resolution | Last-Write-Wins (LWW by Clock) | Vector Clocks with Add-Wins Union | Add-Wins Union: Clock drift in LWW can silently erase items a user added; Add-Wins guarantees no purchased item is lost. |
| Inventory Reservation | Reserve stock upon Cart Addition | Reserve stock only upon Checkout | Reserve at Checkout: Prevents abandoned carts from tying up physical warehouse inventory. |
| Guest Cart Merging | Synchronous Block on Login | Asynchronous Background Worker | Synchronous on Login: Ensures the very next screen the user sees contains their full combined cart. |
6. What is Expected at Each Level?
Mid-Level (L4 / IC4)
- Identifies the need to store carts for both anonymous guests (via cookies) and logged-in users.
- Designs basic schemas for cart headers and cart item rows.
- Explains why inventory is not reserved until checkout.
Senior (L5 / IC5)
- Explains the Dynamo philosophy: why e-commerce carts must be "always writable" (, AP mode).
- Details guest-to-user cart merging logic and duplicate resolution rules.
- Explains vector clocks and sibling reconciliation (Add-Wins policy).
- Separates cart persistence from the live checkout pricing and reservation pipeline.
Staff+ (L6 / Principal)
- Mathematically evaluates CRDT convergence models (PN-Counters and OR-Sets - Observed-Remove Sets) for lock-free cart replication.
- Formulates multi-region active-active deployment topologies with Anycast DNS routing to topologically closest datacenters.
- Formulates strategies for preventing cart bloating attacks (malicious bots adding 10,000 items to exhaust memory).
- Designs automated TTL archival policies for abandoned carts to keep primary memory caches lean.
