Amazon (Marketplace)
This is not the cart-and-checkout problem - that's its own page. This one is one level up: a catalog where the same product can be sold by several sellers at several prices, and an order has to pick a seller and take stock out of that seller's inventory, not some single global count.
Requirements
Functional
- A
CataloglistsProducts; each product can have multipleListings, one perSeller, each with its own price and stock count. - A customer places an
Orderfor a product; the system picks a listing to fulfill it from (cheapest available, in this design) and decrements that seller's stock. - A
Sellercan update the price or stock of their own listings without touching anyone else's. - An order can span multiple products from different sellers and still be one order the customer sees as a whole.
Non-functional
- Picking a listing must never oversell a seller's stock, even when several orders for the same product arrive close together.
- Adding a new way to rank listings (fastest shipping, best seller rating) should not
require changing
OrderorCatalog.
Design
Catalog maps a product to its listings; it never touches money or stock directly. Order
asks a ListingSelectionStrategy to pick a listing per line item, then calls that listing's
reserveStock - the only place stock actually decreases. A Seller owns its listings the
same way ParkingFloor owns its spots: nothing outside touches a Listing's stock field
except through that one method.
- 1The customer orders a product, not a specific seller.
- 2The order asks the catalog for every seller currently offering this product.
- 3Which listing wins is delegated entirely - order placement never hardcodes "cheapest".
- 4Only the chosen listing's stock ever changes; every other seller's count is untouched.
- 5The listing confirms the reservation before the order is finalized.
Splitting Product (the catalog entry: title, description, category) from Listing (one
seller's price and stock for that product) is what makes "three sellers, three prices" a
non-event instead of a special case bolted onto Product.
Class diagram
Code
Design decisions
Listingis a separate class fromProduct, not aMap<Seller, Price>field onProduct. A listing has its own lifecycle - a seller can deactivate theirs while others stay live - and its own stock count that changes independently. Modeling it as a first-class object givesreserveStocksomewhere to live; a bare map would push that logic back up intoOrderorCatalog, whichever touched it first.reserveStocklives onListing, and it's the only mutator of stock in the whole system. Every path that reduces stock - a placed order, a returned order putting it back - goes through this one method, so the invariant "stock never goes negative" has exactly one place to be enforced instead of N.- Listing selection is a
ListingSelectionStrategy, mirroringFeeStrategyfromparking-lot.mdx. Cheapest-first is one policy; a real marketplace also weighs shipping speed, seller rating, and Prime eligibility. None of that should changeOrder's placement logic, only which strategy gets wired in. - What's missing for a real system:
reserveStockhere isn't atomic across concurrent orders - a real implementation needs a compare-and-swap or DB-level row lock on the listing's stock count so two orders racing for the last unit can't both succeed, and a reservation needs a timeout/release path for abandoned carts rather than committing stock the instant a listing is picked.