System Design Problem

Design a Shopping Cart System

Commonly Asked By:AmazonWalmartShopifyeBay

Interview Setup

Interview Prompt

Design an e-commerce shopping cart system supporting add, update, and remove actions, guest and logged-in carts, login cart merging, and price validation at checkout.

Clarifying Questions (ask before designing)

QuestionWhy it matters
Guest vs logged-in storage strategy?20M guest carts per day using client localStorage and Redis compared to 50M persistent user carts in Redis.
Price capture at add time vs checkout?Price at add time is recorded in the cart line item, whereas checkout revalidates against the catalog service.
Cart durability requirements?Redis serves as primary fast store with asynchronous PostgreSQL backups every 5 minutes for disaster recovery.
Peak cart operations per second?Average 6K operations per second scaling to 50K during promotional sales determines Redis cluster shard sizing.

Scope

In scope

  • Add, update, and remove cart items
  • Guest cart management
  • Login cart merge
  • Checkout price validation
  • Save for later wishlist
  • Cart abandonment events

Out of scope (state explicitly)

  • Inventory reservation lifecycle
  • Payment processing
  • Recommendations

Functional Requirements

Start by aligning with your interviewer on the system scope. For an e-commerce shopping cart, confirm guest vs logged-in behaviors, cart merge semantics on login, and whether price revalidation or checkout inventory reservation belongs in-band.

  • Add and remove items: Add products with specified quantities, update quantities dynamically, and remove items.
  • Persist cart across sessions: Ensure a logged-in user cart survives application termination and synchronizes across multiple devices.
  • Guest cart support: Allow anonymous visitors to accumulate items without authentication and merge items cleanly upon login.
  • Price and availability validation: Recheck catalog prices and inventory stock at checkout time because carts can sit idle for weeks before purchase. For stock reservation mechanics, see the Inventory Management System.
  • Saved for later: Allow users to move items out of the active cart into a persistent secondary wishlist.
  • Configurable cart expiry: Automatically expire abandoned guest and user carts according to retention policies (such as 30 days).
  • Promotions and discounts: Apply coupon codes, bundle pricing, and display itemized order discounts.
  • Multi-seller carts: Support items sourced from multiple vendors within one cart, displaying per-seller subtotals before handoff to the Order Management System.
  • Shipping estimation: Calculate estimated shipping fees and delivery timelines based on item dimensions and destination zip code.
  • Cart sharing: Allow customers to export and share read-only cart links for gift registries and collaboration.

Non-Functional Requirements

Interviewers focus primarily on low-latency mutations and consistent cross-device synchronization. Introducing an in-memory key-value pattern early justifies sub-50 ms updates without tying connections to session affinity.

  • Low Latency: Process cart mutations (add, update, delete) in under 50 ms p99, with cart read latencies under 5 ms.
  • High Availability: Achieve 99.99% uptime because cart service degradation immediately stalls checkout conversion and revenue.
  • Consistency: Guarantee real-time cross-device consistency for logged-in accounts using centralized session storage.
  • Scale: Support 50M daily active cart users and handle 500M daily operations, scaling up to 50K operations per second during major events such as a Flash Sale System.
  • Durability: Ensure cart state survives Redis server restarts via append-only file (AOF) persistence and periodic relational database snapshots.
  • Stateless Application Layer: Eliminate sticky sessions so any API gateway or cart worker instance can serve any incoming request.
  • Idempotency: Ensure duplicate network requests or retries do not inadvertently increment item quantities.

Capacity Estimations

Calculate memory and throughput bounds before finalizing the cluster topology. Active daily users and operations per user determine partition count, while average item volume dictates memory sizing.

MetricCalculationValue
DAU with active cartsGiven (assumption documented in value)50M
Cart operations / day50M users x ~10 ops/day500M
Cart operations / sec500M ÷ 86400~6K (peak 50K during sales)
Avg items per cartGiven (typical workload assumption)5
Cart data per userGiven~2 KB
Total cart data50M x 2 KB100 GB
Guest carts / dayGiven (assumption documented in value)20M (many abandoned)
Cart-to-order conversionGiven~10%

Architecture Diagram

In the room: mention the guest-to-authenticated cart merge early, because atomic Lua scripts on login represent a classic follow-up topic.

Guide the interviewer through the guest vs logged-in architectural split. Authenticated carts reside directly in Redis for fast reads, whereas anonymous guests retain state on-device until login triggers an atomic merge. Final checkout rechecks prices and item stock against catalog and inventory clusters, separated into a dedicated validation hop.

Loading...

Component Deep Dives

Cart Storage Architecture: Redis Hash Pattern

In-Memory Key-Value Storage

Cart reads occur on almost every storefront page navigation, so authenticated cart data is maintained in Redis Hashes with sub-millisecond access. For a broader overview of caching tiers, review Redis Patterns and Caching Patterns. Anonymous guest carts remain on the client device until login triggers an atomic Lua script merge into the primary user key.

Event Bus Design (Kafka)

Cart state changes stream asynchronously to Kafka for analytics and relational snapshots, completely decoupled from the latency-sensitive add-to-cart path.

Topic: cart-events
  Partitions: 64
  Partition key: user_id (preserves per-user cart event ordering)
  Retention: 7 days
  Replication factor: 3, min.insync.replicas: 2

Producer: Cart Service on mutation (idempotent producer)
  Event: { event_id, user_id, action: "add" | "remove" | "update" | "checkout" | "abandon", items[], total_value, timestamp }

Consumer groups:
  1. abandonment-recovery: triggers recovery emails if no checkout occurs within 1 hour (suppressed if purchased or cart < $10)
  2. analytics: ingests cart funnels into ClickHouse to evaluate drop-offs
  3. recommendation: mines "frequently added together" co-occurrence patterns
  4. pg-backup: asynchronous snapshots from Redis cart to PostgreSQL every 5 minutes

Sync path: cart CRUD in Redis executes in under 5 ms; cart service publishes event asynchronously.
Async path: abandonment emails and analytics processing never block the add-to-cart request.
DLQ: cart-events-dlq, alerting when abandonment consumer lag exceeds 300 seconds.

Guest Cart to Login Merge

Atomic Merge Workflow

Merging guest carts into existing user carts is a critical edge case. Interviewers expect an atomic transaction model rather than vulnerable read-modify-write patterns that risk race conditions.

When a guest with 3 items signs in to an account that already contains 2 items, the service merges entries by taking the maximum quantity for overlapping SKUs. Executing this logic via a Redis Lua script ensures complete atomicity against concurrent login sessions.

Scenario:
  1. Guest adds 3 items to cart (stored by session_id):
     cart:guest-session-abc -> { SKU-1: qty 1, SKU-2: qty 2, SKU-3: qty 1 }

  2. Guest logs in as user-123 with existing cart:
     cart:user-123 -> { SKU-2: qty 1, SKU-4: qty 3 }

  3. Merge strategy:
     For items present in BOTH carts (SKU-2):
       Option A: Keep higher quantity -> qty = max(2, 1) = 2
       Option B: Sum quantities -> qty = 2 + 1 = 3
       Option C: Retain guest cart quantity as most recent intent -> qty = 2
       
       Standard e-commerce convention selects Option A (max quantity) for optimal user experience.

     For items only in guest cart: insert into user cart
     For items only in user cart: retain unchanged

     Result: cart:user-123 -> { SKU-1: qty 1, SKU-2: qty 2, SKU-3: qty 1, SKU-4: qty 3 }

  4. Delete guest cart: DEL cart:guest-session-abc

Price Validation and Stale Data Handling

Checkout Price Verification

Shopping carts frequently sit untouched for days or weeks, making product price changes and inventory depletion inevitable. Clarify your stale-pricing policy before detailing batch lookup flows.

The system adopts a hybrid resolution policy: capture item price at initial add time, then reconcile against current catalog rates during checkout. If the current price is lower, the customer receives the discount. If higher, the UI flags the increase and prompts confirmation before charging.

Implementation:
  On cart page load:
    cart_items = redis.hgetall("cart:user-123")
    current_prices = catalog_service.batch_get_prices([sku_ids])
    
    for item in cart_items:
      item.current_price = current_prices[item.sku_id]
      item.price_changed = (item.current_price != item.price_at_add)
      item.in_stock = inventory_service.check_stock(item.sku_id)
    
    return cart with annotations

API Design

Cart Mutation and Query Endpoints

All endpoints enforce idempotent semantics. Line items identify by product SKU, and modifications validate through standard REST verbs.

Add to Cart

HTTP
POST /api/v1/cart/items
{
  "sku_id": "SKU-456",
  "quantity": 2,
  "seller_id": "seller-1"
}
Response: 200 OK
{
  "cart_id": "cart:user-123",
  "item_count": 5,
  "item": {"sku_id": "SKU-456", "quantity": 2, "price": 29.99, "seller_id": "seller-1"}
}

Get Cart

HTTP
GET /api/v1/cart
Response: 200 OK
{
  "items": [
    {"sku_id": "SKU-456", "name": "Wireless Mouse", "quantity": 2,
     "price_at_add": 29.99, "current_price": 29.99, "price_changed": false,
     "in_stock": true, "seller": "TechStore", "image": "https://cdn..."}
  ],
  "subtotal": 59.98,
  "item_count": 5,
  "promotions_applied": [{"code": "SAVE10", "discount": -5.99}],
  "estimated_total": 53.99
}

Update Item Quantity

HTTP
PUT /api/v1/cart/items/{sku_id}
{ "quantity": 3 }
Response: 200 OK

Remove Item

HTTP
DELETE /api/v1/cart/items/{sku_id}
Response: 200 OK

Merge Guest Cart

HTTP
POST /api/v1/cart/merge
{ "guest_session_id": "session-abc" }
Response: 200 OK
{ "merged_items": 3, "conflicts_resolved": 1 }

Common Error Responses

The cart service surfaces structured client error payloads for malformed items, expired sessions, and validation failures.

400 Bad Request: invalid input, missing required fields, or malformed JSON payload
401 Unauthorized: missing or invalid authentication token or API key
403 Forbidden: authenticated caller lacks required permissions for this resource
404 Not Found: requested resource ID does not exist
409 Conflict: duplicate write or version conflict, retry with a unique idempotency key
422 Unprocessable Entity: syntactically valid request failed semantic business validation
429 Too Many Requests: rate limit quota exceeded, client should honor Retry-After header
500 Internal Error: unexpected server failure, retry safely with an idempotency key
503 Service Unavailable: downstream dependency is unavailable or overloaded, retry with exponential backoff
402 Payment Required: account balance or payment method has insufficient funds
502 Bad Gateway: payment gateway provider timeout, poll transaction status endpoint

Data Model

Redis Primary Cart Store

User and guest carts utilize Redis Hash structures to provide O(1) mutations and reads per line item without serializing the full cart array.

# User cart (Hash)
cart:{user_id} -> Hash {
  SKU-456: '{"qty":2,"price_at_add":29.99,"seller_id":"s-1","added_at":"2026-03-14T10:00:00Z"}',
  SKU-789: '{"qty":1,"price_at_add":14.99,"seller_id":"s-2","added_at":"2026-03-14T10:05:00Z"}'
}
TTL: 2592000 (30 days, refreshed on activity)

# Guest cart
cart:guest-{session_id} -> Hash (same structure)
TTL: 86400 (1 day, shorter TTL for anonymous guests)

# Saved for later
saved:{user_id} -> Hash { SKU-123: '{"saved_at":"...","price":29.99}' }
TTL: 7776000 (90 days)

# Cart item count (for badge display to avoid HLEN on every page)
cart_count:{user_id} -> INT
TTL: 2592000

PostgreSQL Backup and Analytics Schema

Asynchronous workers persist snapshot records into relational tables to power recovery tooling and cart abandonment analytics.

SQL
CREATE TABLE cart_snapshots (
    user_id         UUID NOT NULL,
    sku_id          VARCHAR(50) NOT NULL,
    quantity        INT NOT NULL,
    price_at_add    DECIMAL(10,2),
    seller_id       VARCHAR(50),
    added_at        TIMESTAMPTZ,
    updated_at      TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (user_id, sku_id)
);

-- Asynchronous sync from Redis to PostgreSQL every 5 minutes
-- Used for: analytics (abandonment analysis) and Redis disaster recovery

Kafka Event Streaming Specifications

Cart state transitions publish to streaming topics to feed recommendation systems and customer recovery campaigns.

Topic: cart-events (actions: add, remove, update, checkout, abandon)
  Consumed by: analytics pipeline, recommendation engine ("frequently added together")

Fault Tolerance

ConcernSolution
Redis node failureRedis Cluster with 6 or more nodes, AOF persistence, and data replicated across shards.
Redis total failureFall back to PostgreSQL backup with slightly stale state, then rebuild Redis keys from PostgreSQL.
Cart data lossAsynchronous backup to PostgreSQL every 5 minutes, with client-side localStorage as last resort.
Duplicate add-to-cartIdempotent mutations because HSET overwrites identical keys instead of creating duplicate records.
Price discrepancyAlways validate at checkout and display clear price drift warnings on the cart page.
Race condition during mergeRedis Lua script for atomic merge so concurrent merge attempts cannot collide.
Abandoned cart recoveryKafka event published after 1 hour of cart inactivity to trigger notification workflows.

Additional Considerations

Interview Walkthrough

  • 25-minute pacing strategy

    Prioritize core architecture before diving into deep multi-region or analytics pipelines.

    • Cart modeled as session-scoped Redis Hash with 30-day TTL (5 min)
    • Price-at-add semantics stored with each line item (6 min)
    • Guest carts with 1-day TTL and atomic Lua merge on login (5 min)
    • Add, update, and remove mutations via O(1) Hash operations with version tracking (5 min)
    • Asynchronous PostgreSQL snapshots via Kafka for disaster recovery (4 min)
  • Frame the cart as a session-scoped, high-churn key-value store using a Redis Hash per user with a 30-day TTL refreshed on activity.
  • Explain price-at-add semantics: store the initial price when the item enters the cart, then flag price_changed on read without silently updating totals.
  • Cover guest carts with shorter TTL (1 day) and the merge flow on login, resolving quantity conflicts by summing or keeping the higher quantity.
  • Walk through add, update, and remove as O(1) Hash operations with a separate cart_count key for header badge display without full hash scans.
  • Detail asynchronous PostgreSQL snapshots via Kafka for analytics and disaster recovery, keeping Redis as primary while PostgreSQL serves as a backup rather than residing on the hot path.
  • Discuss multi-seller carts: each line item carries seller identification so checkout can split line items into separate fulfillment orders in the Order Management System.
  • Common pitfall: storing the cart in session cookies or JWT tokens introduces strict size limits, prevents server-side cart merging, and eliminates cross-device synchronization.

Engineering Trade-offs

Storage Engine Comparison

Cart storage balances durability against throughput, leveraging Redis for hot mutation paths and PostgreSQL for audit trails and abandoned-cart analytics.

Redis (Recommended):
  ✓ Sub-millisecond latency
  ✓ Hash data structure matches cart operations directly
  ✓ Native TTL for automatic expiration
  ✓ Lua scripting for atomic operations (merge, validate)
  ✗ In-memory storage (requires AOF and RDB for persistence)
  ✗ Higher RAM cost at 100 GB scale

DynamoDB:
  ✓ Fully managed persistence and replication
  ✓ Pay-per-request pricing suitable for variable loads
  ✓ Automatic scaling
  ✗ Higher latency (5 to 10 ms compared to sub-millisecond Redis)
  ✗ No TTL granularity per hash field (TTL applies per item)
  ✗ Item size limit of 400 KB

Server-side session (e.g., PostgreSQL):
  ✓ Fully durable with ACID guarantees
  ✗ Much higher latency (10 to 50 ms)
  ✗ Frequent cart reads create a database bottleneck
  ✗ Relational schemas are suboptimal for rapid key-value access

Client-side (localStorage):
  ✓ Zero server cost for guest carts
  ✓ Operates offline
  ✗ Does not synchronize across devices
  ✗ Lost if browser data is cleared
  ✗ Lacks server-side analytics visibility

Best practice:
  Guest carts: localStorage on client with asynchronous backup to Redis on key actions
  Logged-in carts: Redis as primary store with asynchronous backup to PostgreSQL
  This strategy minimizes Redis memory consumption while sustaining low read latency.

Cart Abandonment Strategy

Recovering abandoned carts requires identifying inactivity thresholds and orchestrating targeted notification workflows without generating spam.

Industry benchmark: approximately 70% of shopping carts are abandoned without checkout.

Detection:
  Cart contains items with no checkout activity for 1 hour -> Flagged as abandoned.
  
  Kafka event: { user_id, cart_items, total_value, abandoned_at }
  Triggers downstream workflows:
    1. Email reminder after 1 hour: "You left items in your cart"
    2. Push notification after 4 hours: "Your cart is waiting"
    3. Email with discount after 24 hours: "10% off your cart with code COMEBACK10"
    4. Final reminder after 72 hours: "Items in your cart may sell out soon"
  
  Suppression rules:
    - User has opted out of marketing communications.
    - Cart total value is under $10 (email cost exceeds expected margin).
    - User has completed an order since the abandonment event.

Analytics:
  Track conversion rates at each checkout funnel stage (cart to shipping to payment to confirmation).
  Identify the specific drop-off step with highest friction.
  Conduct A/B testing on cart layouts and incentives to lift conversion rates.

💬Review

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...