Javathoughts Logo
Javathoughts
Published on
Views

Design E-commerce Cart, Order, and Inventory System Design Interview Guide

Authors
  • avatar
    Name
    Javed Shaikh
    Twitter

← System Design Interview Preparation

This guide walks through Design E-commerce Cart, Order, and Inventory the way you would in a backend or Java interview. The numbers are interview estimates. They help you show your thinking. They are not a production capacity plan.


1. Problem

Shoppers browse products, fill a cart, check out, and expect the item they paid for to exist in a warehouse.

Example:

  • Add a phone case, change quantity, remove a charger
  • Checkout: reserve stock, create an order, take payment
  • If payment fails, release the reservation
  • If they abandon the cart, it expires
  • Two people must not buy the last unit

This is the core of the longer e-commerce platform case study, focused on cart vs inventory races.


2. Functional Requirements / FR

RequirementWhat it means
Browse catalogProduct pages, price, availability hint.
Cart add / update / removePer user or guest cookie.
CheckoutSnapshot prices, shipping, totals.
Inventory reservationHold units for a few minutes.
Order creationDurable order with line items.
PaymentCharge after (or during) reservation.
Stock deductionConvert reservation to sold.
Cancel / refundRestock if not shipped.
Cart expiryIdle carts die; reservations die faster.

Out of scope: full search ranking, ads, warehouse robotics, tax engines in depth.


3. Non-Functional Requirements / NFR

RequirementWhy it matters
No oversellLast-item races are the interview test.
Idempotent checkoutDouble tap ≠ two orders.
Cart is eventually consistentFine if quantity UI lags a second.
Checkout is strongly consistent on stockReservation must be real.
Peak sale trafficFlash sale QPS is bursty.
Guest + logged-in mergeCart merge after login.

Interview line: cart is a cache of intent. Inventory is the truth for stock. Order is the truth after pay.


4. Back-of-the-Envelope Calculation

Traffic assumptions

  • 50 million DAU
  • Average 20 product views / user / day (browse)
  • 5% add to cart, 2% of DAU check out → 1 million checkouts/day
  • Read/write: browse is read-heavy; checkout is write-critical

QPS

Browse:

50,000,000 × 20 = 1,000,000,000 views/day
1,000,000,000 / 86,400 ≈ 11,600 QPS
If peak is 5x, design for around 58,000 QPS (CDN + cache).

Checkout:

1,000,000 / 86,400 ≈ 12 QPS average
If peak is 5x, design for around 60 QPS of checkouts.
Flash sale: say 20x → around 240 QPS. Still about inventory, not CPU.

Cart updates might be 10× checkout (people fidget). Plan ~600 QPS cart writes at peak.

Storage

Cart JSON ≈ 2 KB, 10 million active carts → 20 GB.

Orders 1M/day × 2 KB ≈ 2 GB/day.

Inventory: millions of SKUs, small rows. Not the disk problem.

Cache / memory

Hot product pages in CDN/Redis. Stock counters for flash SKUs in Redis with DB as source.

Servers

Browse: many stateless boxes behind cache. Checkout: fewer, careful DB. Interview estimate: tens of catalog servers, a handful of order/inventory nodes, not exact production numbers.


5. APIs

GET    /v1/products/{sku}
POST   /v1/carts/{cartId}/items     { sku, qty }
PATCH  /v1/carts/{cartId}/items/{sku}
DELETE /v1/carts/{cartId}/items/{sku}

POST   /v1/checkout
Idempotency-Key: <uuid>
{ "cartId", "addressId", "paymentMethodToken" }

GET    /v1/orders/{orderId}
POST   /v1/orders/{orderId}/cancel

Inventory is internal:

POST /internal/inventory/reservations  { sku, qty, ownerId, ttlSeconds }
POST /internal/inventory/reservations/{id}/commit
POST /internal/inventory/reservations/{id}/release

Errors: 409 out of stock, 409 idempotency conflict, 410 cart expired.


6. Data Model

carts / cart_items

cart_id, user_id nullable, guest_id, updated_at, TTL. Items: sku, qty, price_hint.

products

Catalog: title, price, images. Availability is not only a boolean on this row at scale.

inventory

FieldNotes
skuPK
on_handPhysical
reservedHeld
availableon_hand - reserved (or computed)
versionOptimistic lock

reservations

reservation_id, sku, qty, order_id nullable, expires_at, status (open, committed, released).

orders / order_lines

Snapshot name, sku, unit_price, qty. payment_id, status, idempotency_key.

Do not re-read catalog prices after the order exists.


7. High-Level Design

Checkout is a short workflow: reserve → pay → commit. Everything else is events.

E-commerce cart, order, and inventory architecture

E-commerce cart, order, and inventory architectureCart is cheap and short-lived. Checkout reserves inventory, creates an order, then charges. A worker handles fulfillment and notifications.checkoutreservechargeorder event👤User🌐API Gateway🛒Cart Service⚙️Order Service📦Inventory Service🗄️Inventory DB💳Payment Service📩Queue👷Fulfillment Worker🔔Notification
Cart is cheap and short-lived. Checkout reserves inventory, creates an order, then charges. A worker handles fulfillment and notifications.

Components:

  • User → API Gateway → Cart Service
  • Order Service on checkout
  • Inventory Service + Inventory DB
  • Payment Service
  • Queue → Fulfillment Worker → Notification

Happy path:

  1. Browse cached catalog.
  2. Cart writes are cheap (Redis or DB).
  3. Checkout: create order pending, reserve stock with TTL (e.g. 10 minutes).
  4. Payment succeeds → commit reservation (reserved ↓, on_hand ↓) → paid.
  5. Event to fulfillment.
  6. Failure or expiry → release reservation.

Why reserve instead of decrement immediately? Payment can fail. You would hide stock from others forever.


8. Deep Dives

Cart expiry

Idle 7–30 days. Reservations are minutes, not days. Do not reserve on add-to-cart for a normal store (only for flash sales if product demands it).

Inventory race

Two checkouts, 1 unit:

  • UPDATE inventory SET reserved = reserved + 1 WHERE sku=? AND on_hand - reserved >= 1
  • Rows affected 0 → 409

Use a transaction or a single conditional update. Redis DECR alone is not enough unless Redis is the agreed source for that flash SKU and you persist later.

Payment

Call payment system with checkout idempotency key. If pay succeeds and commit fails, a worker commits or refunds. Never leave “paid but unreserved” silent.

Cancellation / refund

If not shipped: release remaining reservation or increment on_hand. If shipped: no restock without warehouse confirm.

Guest merge

On login, union carts. If both have the same sku, sum qty then clamp to available at checkout, not at merge.

Flash sale

Hot SKU: serialize with a queue per sku, or Redis stock + Lua check, plus a waiting room. Same idea as ticket locks.


9. Bottlenecks

  • Hot SKU row in SQL
  • Cart Redis memory on huge guest carts
  • Payment latency inside checkout
  • Oversell from cache-only stock on PDP (“3 left” is a hint, not a lock)

Mitigate: per-sku queues, cache for reads only, conditional writes for stock.


10. Tradeoffs

ChoiceUpsideDownside
Reserve on add-to-cartFeels reservedLots of fake holds
Reserve on checkoutHonest stockLast-second 409
Soft oversell + cancelHigher conversionAngry customers
Single inventory DBSimple correctnessScale limits

Pick: hint on PDP, reserve at checkout with TTL, conditional SQL/Redis, idempotent order.


11. Failure Modes

FailureHandling
Paid, reserve commit failedWorker retries commit; else refund
Reservation TTL while user paysExtend TTL on payment start, or fail checkout
Duplicate checkoutSame idempotency key
Inventory service downFail checkout, do not guess stock
Fulfillment worker downOrder stays paid; catch up from queue

12. Interview Answer in 10 Minutes

"I would split browse, cart, inventory, and order.

Browse is 50 million users times 20 views: a billion reads a day, about 12,000 QPS, about 60,000 at 5× peak — CDN and cache. Checkout is 1 million a day, about 12 QPS, about 60 at peak. The design problem is the last item, not throughput.

Cart is short-lived intent. Checkout uses an idempotency key, snapshots prices, and reserves inventory with a TTL using a conditional update so two buyers cannot take the same unit. Then we pay. On success we commit the reservation and emit a fulfillment event. On failure or timeout we release.

Product page stock is a hint. The reservation is the lock."


13. Interview Talking Points

  • Cart ≠ inventory.
  • Conditional decrement / reservation.
  • TTL holds.
  • Idempotent checkout.
  • Paid-but-failed-commit reconciliation.
  • Metrics: oversell count (must be ~0), checkout 409 rate, reservation expiry rate, payment success.
  • Classic Java services with Kafka after pay.

14. Follow-up Questions

SQL vs Redis for stock?
SQL for correctness at moderate QPS. Redis for flash SKUs with persistence and reconciliation.

Warehouse multi-location?
Reserve against a node or a pool; split inventory by warehouse_id.

Price change in cart?
Reprice at checkout. Cart prices are hints.

Spring?
Order saga or outbox: reservation, payment, commit. See event-driven Java tradeoffs.


Related JavaThoughts reading:


Next in this series: Design a Ticket Booking System.