Problem statement
Design the object model and core APIs for the shopping-cart component of an online store. The cart holds the goods a shopper has picked but not yet paid for: it tracks line items and their quantities, lets the shopper add, remove, and change quantity, checks that stock exists before an item goes in, and computes running totals with a hook for discounts. Whatever sits above it — a web controller, a mobile client, or a test harness — calls these APIs and reads state back.
Operating context. One shopper's cart at a time, driven in-process by a single caller. A catalog supplies each product's current unit price and available stock; the cart does not own the catalog and must ask it. A line item is a (product, quantity, captured unit price) triple, and the same product added twice merges into one line with a summed quantity rather than a duplicate row. Totals are a subtotal (sum of line prices), an optional discount, and a grand total. Discounting is open-ended — today a percent-off code, tomorrow a buy-one-get-one or a threshold rule — so the cart must call out to a pricing rule instead of hard-coding the arithmetic. The cart's contents must survive being set down and picked back up later, so storage sits behind an abstraction rather than being wired to any one database.
Out of scope. Checkout, payment, and order placement; the catalog / inventory service internals (assume it answers price and stock queries); user accounts and authentication; the HTTP or UI layer; and multi-currency or tax computation. Design only the in-process cart object model and its collaborators, not the transport, storage engine, or catalog behind them.
What to produce. The class hierarchy (entities such as Cart and LineItem, value objects such as Money and Quantity, and the collaborator interfaces for catalog, pricing, and persistence), the public API each class exposes, and the state a cart moves through as items come and go. Be explicit about: how adding an already-present product merges into the existing line, how a stock check gates every add and quantity increase, how the discount hook is abstracted so a new rule drops in without editing the cart, how subtotal and grand total are recomputed, and how the persistence seam keeps the cart storage-agnostic and unit-testable.
Functional requirements
- Add a product with a quantity: merge into the existing line if the product is already in the cart, otherwise create a new line item.
- Remove a line item entirely, or decrease a line's quantity, updating the cart's contents accordingly.
- Update a line item's quantity to an exact value, treating a change to zero as a removal.
- Check available stock via the catalog before any add or quantity increase, and reject the change when stock is insufficient.
- Compute the subtotal, apply the active discount rule through a pricing hook, and expose the resulting grand total.
- Load and save the cart's contents through a persistence abstraction so it survives across sessions.
Non-functional requirements
- Adding, removing, or updating a line is O(1) in the number of distinct products via a product-keyed lookup, not a linear scan.
- The discount hook is pluggable: adding a new pricing rule must not modify the Cart or LineItem classes.
- The catalog, pricing rule, and persistence store are injected interfaces, so a fake substitutes for each in a unit test.
- Money and Quantity are value objects with no floating-point rounding drift; totals are exact to the minor currency unit.
- Every mutation leaves the cart in a valid state: no negative or zero quantities linger, and totals always reflect current lines.
- The cart core is unit-testable with no database, no network, and no wall clock — items in, totals out.
Topics
- System Design LLD
- Oop Solid
- Patterns Strategy
- Extensibility
- Testability Clock