System Design Interview: Design an E-Commerce Order and Checkout System

What Is an E-Commerce Order System?

An e-commerce order system manages the full lifecycle of an online purchase: cart management, checkout, inventory reservation, payment processing, and order fulfillment. Examples: Amazon Orders, Shopify checkout, eBay purchases. Core challenges: concurrent inventory management (two buyers purchasing the last item), payment atomicity, and order state machine across distributed services.

  • Lyft Interview Guide
  • Coinbase Interview Guide
  • DoorDash Interview Guide
  • Airbnb Interview Guide
  • Stripe Interview Guide
  • Shopify Interview Guide
  • System Requirements

    Functional

    • Shopping cart: add/remove items, persist across sessions
    • Checkout: reserve inventory, compute total, process payment
    • Order management: view, cancel, track fulfillment status
    • Inventory management: track stock, prevent overselling

    Non-Functional

    • 100M users, 10K checkouts/second at peak (Black Friday)
    • No overselling: never confirm an order when stock is 0
    • Payment atomicity: charge once per order

    Core Data Model

    products: id, name, price_cents, sku
    inventory: product_id, warehouse_id, quantity_available, quantity_reserved
    carts: id, user_id, created_at, updated_at
    cart_items: cart_id, product_id, quantity, price_at_add
    orders: id, user_id, status, subtotal, tax, shipping, total, created_at
    order_items: order_id, product_id, quantity, unit_price
    payments: id, order_id, amount, status, idempotency_key, provider_charge_id
    

    Cart Implementation

    Carts need fast read/write and do not require ACID guarantees. Store in Redis (TTL 30 days):

    HSET cart:{user_id} product_id:123 quantity:2
    HSET cart:{user_id} product_id:456 quantity:1
    EXPIRE cart:{user_id} 2592000  # 30 days
    

    On checkout: read the cart from Redis, validate prices against current product prices (prices may have changed), then persist as an order in the DB.

    Checkout Flow — Preventing Overselling

    BEGIN TRANSACTION;
    -- 1. Lock inventory rows for all items in the cart
    SELECT quantity_available, quantity_reserved
    FROM inventory
    WHERE product_id IN (123, 456) AND warehouse_id = ?
    FOR UPDATE;
    
    -- 2. Check each product has sufficient quantity
    -- (quantity_available - quantity_reserved >= ordered_quantity)
    
    -- 3. Reserve inventory (not yet deducted -- reserved until fulfilled)
    UPDATE inventory
    SET quantity_reserved = quantity_reserved + ordered_qty
    WHERE product_id = ?;
    
    -- 4. Create order record
    INSERT INTO orders (...) VALUES (...);
    INSERT INTO order_items (...) VALUES (...);
    COMMIT;
    

    If any product check fails: rollback with an “out of stock” error. The transaction ensures atomicity — two concurrent checkouts for the last item both acquire the lock; one succeeds, the other gets the updated (insufficient) quantity and rolls back.

    Payment Processing

    After inventory is reserved, charge the payment method:

    charge = stripe.charge.create(
        amount=total_cents,
        customer=stripe_customer_id,
        idempotency_key=f"order-{order_id}"
    )
    

    On payment success: update order status to CONFIRMED, send confirmation email. On payment failure: release the reserved inventory (decrement quantity_reserved), update order status to PAYMENT_FAILED.

    Order State Machine

    PENDING_PAYMENT → CONFIRMED → PROCESSING → SHIPPED → DELIVERED
          │                │
      PAYMENT_FAILED    CANCELLED (before shipment)
    

    Inventory at Scale

    Sharding inventory by product_id distributes write load. But for flash sales (Nike shoe drop: 10K buyers competing for 1K pairs), the single-product inventory row becomes a hotspot with thousands of concurrent locks. Solutions:

    • Inventory partitioning: split 1K units across 10 virtual warehouse rows (100 units each). Random assignment distributes lock contention 10x.
    • Pre-sale reservation: allow users to queue, assign inventory in batches every 30 seconds.
    • Optimistic locking with retry: use version column instead of FOR UPDATE; retry on conflict (works for low contention, not flash sales).

    Interview Tips

    • Separate cart (Redis) from orders (SQL) — different consistency requirements.
    • quantity_reserved vs quantity_available prevents deducting before payment confirms.
    • Idempotency key = order_id ensures exactly-one charge.
    • Inventory partitioning for flash sales shows awareness of hotspot problems.
    Scroll to Top