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.
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.