## System Design: Coffee Ordering System
Design a system for a coffee shop (or chain) that supports ordering drinks.
### Core use cases
- Customer browses menu and places an order (pickup or in-store).
- Customer customizes items (size, milk type, add-ons) and sets quantity.
- System calculates total price (including tax/discounts).
- Payment can be processed (or order marked as pay-at-store).
- Baristas see a **queue** of incoming orders and update status: `PLACED → PAID (optional) → IN_PROGRESS → READY → COMPLETED/CANCELLED`.
- Customer can see order status updates.
### Constraints / scale (assume)
- Start with one store, then extend to many stores.
- Peak: thousands of orders/min across all stores.
- Status updates should feel near-real-time to customers.
### What to cover
- APIs (or events) for placing orders and status updates.
- Data model for menu, orders, payments.
- High-level architecture and major services.
- Consistency, idempotency, and failure handling (e.g., retries, duplicate submits).
- How you would scale to many stores.
Quick Answer: This System Design question evaluates a candidate's ability to design scalable, real-time transactional systems covering API design, data modeling for menus and orders, state transitions, payment flow, and operational concerns such as idempotency, consistency, and failure handling.
Solution
### 1) Clarify requirements (fast)
**Functional**
- Menu: categories, items, options/modifiers, pricing rules.
- Order: create, update, cancel; compute totals.
- Payment: online card/wallet (optional), or pay-in-store.
- Store routing: order goes to a specific store (nearest or chosen).
- Production queue: barista view with ordering/priority, status updates.
- Customer tracking: see current status; notifications when ready.
**Non-functional**
- Low latency for create-order and status read (sub-second typical).
- High availability during peak.
- Strong correctness around payment/order submission (avoid duplicates).
- Observability + audit trail.
### 2) High-level architecture
- **API Gateway / BFF** (mobile/web) handling auth, rate limits.
- **Menu Service** (read-heavy): serves menu + pricing config; cached.
- **Order Service** (write-heavy): order creation, line items, state machine.
- **Payment Service**: integrates with PSP; stores payment intents and results.
- **Store/Queue Service**: store-level queue projections; barista UI.
- **Notification Service**: push/SMS/email when status changes.
- **Event Bus** (Kafka/PubSub): `OrderPlaced`, `PaymentCaptured`, `OrderStatusChanged`.
- **Datastores**:
- Orders: relational (Postgres/MySQL) for transactions OR strongly-consistent KV.
- Menu: document store + CDN/cache.
- Queue projections: Redis / Elastic / per-store materialized views.
### 3) Data model (example)
- `stores(store_id, name, timezone, address, hours, capacity_config)`
- `menu_items(item_id, name, base_price, availability)`
- `modifiers(mod_id, name, price_delta, constraints)`
- `orders(order_id, user_id, store_id, status, subtotal, tax, total, created_at, updated_at, idempotency_key)`
- `order_lines(line_id, order_id, item_id, qty, modifiers_json, line_price)`
- `payments(payment_id, order_id, provider, status, amount, currency, provider_ref, created_at)`
- Optional: `order_events(order_id, event_type, payload, created_at)` for audit.
### 4) Key APIs
**Menu**
- `GET /stores/{storeId}/menu`
**Order**
- `POST /orders` (idempotent via header or body `idempotency_key`)
- request: `store_id`, `items[]`, `payment_method`
- response: `order_id`, `status`, `total`
- `GET /orders/{orderId}`
- `POST /orders/{orderId}/cancel`
**Payment**
- `POST /payments/intent` (or inside `POST /orders`)
- Webhook endpoint from PSP: `POST /payments/webhook`
**Barista / status**
- `GET /stores/{storeId}/queue?status=PLACED,IN_PROGRESS,READY`
- `POST /orders/{orderId}/status` (with role-based auth)
**Realtime updates**
- WebSocket/SSE: `GET /orders/{orderId}/events` or push notifications.
### 5) Core flows
**A) Place order (online pay)**
1. Client sends `POST /orders` with `idempotency_key`.
2. Order Service validates items/prices (may re-price server-side), creates `orders` row in `PLACED`.
3. Payment Service creates payment intent; on success sets `PAID`.
4. Emit events to bus; Queue projection updates barista queue.
**B) Status updates**
- Barista changes status; Order Service enforces legal transitions.
- Emit `OrderStatusChanged`; Notification Service informs customer.
### 6) Consistency, idempotency, and failure handling
- **Idempotency key** on order create prevents duplicate orders from retries/double-click.
- Payment integration:
- Prefer **payment intent** pattern; treat PSP webhooks as source of truth.
- Ensure `PaymentCaptured` processing is **idempotent** (dedupe by `provider_ref`).
- **State machine** for order status to avoid invalid transitions.
- Use **outbox pattern** (write order + event atomically) to avoid missing events.
### 7) Scaling to many stores
- Partition/shard by `store_id` or `order_id`.
- Create **per-store queue projections** (Redis sorted sets, or DB materialized view) for fast barista reads.
- Cache menu via CDN; invalidate on menu updates.
- Use async processing for notifications and analytics.
### 8) Security and privacy
- AuthN/AuthZ: customers vs baristas vs admins (RBAC).
- Protect webhooks (signature verification).
- PII minimization; encrypt sensitive fields; do not store raw card data.
### 9) Observability
- Metrics: order create latency, payment success rate, queue length per store, time-in-status (SLA), webhook failures.
- Tracing across Order/Payment/Queue.
### 10) Edge cases to call out
- Item goes out of stock after order: allow cancel/refund flow.
- Store offline: degrade gracefully (queue locally, or reject new orders).
- Refunds/chargebacks: reflect in order/payment state.
- Clock/timezone for “ready time” estimates per store.