Online Bookstore
Design an Amazon-style online bookstore: catalog, search, cart, checkout, orders, and payment. Focus on the domain model and how state moves through the order lifecycle.
Clarifying questions
- Are we modeling inventory and fulfillment, or treating those as external?
- Single-seller marketplace or third-party sellers?
- Do we support guest checkout or accounts only?
- Are prices tied to user (segments, promos) or global?
- Do we model returns/refunds in this iteration?
Core requirements
- Browse and search a catalog of books with metadata and pricing.
- Add and remove items from a per-user cart with quantity control.
- Place an order from cart contents with a shipping address and payment method.
- Track order lifecycle: placed, paid, shipped, delivered, cancelled.
- Process payments through a pluggable gateway.
- Persist user identity, addresses, and order history.
Canonical class breakdown
Catalog — Searchable index of Books with price and availability lookups; abstracts the search backend.
Book — Product entity: ISBN, title, author, price, and stock pointer.
User — Account identity holding addresses, payment methods, and order history references.
Cart — Per-user mutable collection of CartItems; computes subtotal and survives across sessions.
Order — Immutable snapshot taken from Cart at checkout time, with its own line items, shipping, and status.
OrderStatus (state machine) — Encodes legal transitions: Placed -> Paid -> Shipped -> Delivered, with Cancel and Refund branches.
PaymentGateway (interface) — Pluggable payment provider; charge and refund operations return idempotent results.
CheckoutService — Orchestrates the cart-to-order transition: price freeze, stock reserve, payment, order creation.
Java sketchjava
// Java skeleton
enum OrderStatus { PLACED, PAID, SHIPPED, DELIVERED, CANCELLED, REFUNDED }
class Book {
private final String isbn;
private final String title;
private final String author;
private BigDecimal price;
private int stock;
}
class Catalog {
public List<Book> search(String query) { ... }
public Book findByIsbn(String isbn) { ... }
}
class Address {
private final String line1;
private final String city;
private final String postalCode;
}
class User {
private final String userId;
private final String email;
private final List<Address> addresses;
private final List<String> orderIds;
}
class CartItem {
private final String isbn;
private int quantity;
private final BigDecimal priceAtAdd; // freeze on add
}
class Cart {
private final String userId;
private final List<CartItem> items;
public void add(Book b, int qty) { ... }
public void remove(String isbn) { ... }
public BigDecimal subtotal() { ... }
}
class OrderLine {
private final String isbn;
private final int quantity;
private final BigDecimal unitPrice; // immutable snapshot
}
class Order {
private final String orderId;
private final String userId;
private final List<OrderLine> lines;
private final Address shipTo;
private final BigDecimal total;
private OrderStatus status;
public void transitionTo(OrderStatus next) { /* enforce legal transitions */ }
}
interface PaymentGateway {
PaymentResult charge(String idempotencyKey, BigDecimal amount, String userId);
PaymentResult refund(String chargeId, BigDecimal amount);
}
class CheckoutService {
private final PaymentGateway gateway;
private final Catalog catalog;
public Order checkout(Cart cart, Address shipTo, String paymentToken) {
// freeze prices, reserve stock, charge payment, create Order
}
}
Key decisions to defend
- Order is an immutable snapshot of Cart, not a mutable continuation — prices and addresses are frozen at checkout.
- OrderStatus is a state machine, not a free-form string — illegal transitions should be unrepresentable.
- PaymentGateway is behind an interface so you can swap Stripe/Braintree and test with a fake.
- Cart items reference Book by ID and store price-at-add; otherwise price changes silently mutate the cart.
- CheckoutService is the only place that touches payment + inventory + order together — keep it transactional.
Likely follow-ups
- · Add third-party sellers — how does Order model multiple shipments?
- · Support promo codes — where does the discount logic live?
- · Idempotency for double-click checkout — what key do you use?
Dive deeper
This question probes whether you understand the distinction between transient state (Cart) and committed state (Order). Candidates who let Cart become Order end up with mutable historical records and unauditable pricing. The other test is service boundaries: checkout is the load-bearing orchestration that ties payment, inventory, and order creation, and it should be the only place those three meet.