Object-Oriented Design Interview

Course Content

Object-Oriented Design Interview

14 sections · 29 lessons

Grocery Store: scoping the prompt and separating product from stock


"Design a system for a grocery store" is the broadest prompt in the course, and the breadth is the test. This lesson covers the first half of the round: choosing a slice and defending it, writing requirements for that slice, and finding the objects — including the modelling error most attempts make, which has the same shape as one you have already met.

A scoping test wearing a modelling problem's clothesScope in, and build• Checkout at a till• Inventory for one store• Product catalogue and pricingName aloud, then cut• Supplier ordering and logistics• Loyalty, accounting, payroll• Online orders and delivery
The prompt is deliberately unbounded; picking a boundary out loud is the thing being scored.

This is a scoping test wearing a modelling problem's clothes

A grocery store contains a point of sale, inventory, purchasing, suppliers, staff scheduling, loyalty programmes, promotions, returns, waste tracking, refrigeration monitoring, and accounting. All of it is real. None of it fits in forty-five minutes.

So the interviewer is not testing whether you can model a grocery store. They are testing whether you can look at an unbounded problem, choose a coherent slice, say why you chose it, and build that slice well. A candidate who tries to cover everything produces twenty boxes with no depth and fails; a candidate who models checkout and inventory properly and names the rest as deliberately excluded passes.

The four questions that change the model

1. "Point of sale, inventory, or both?" Both, and the boundary between them is where the design gets interesting. If the interviewer says "only the till", the problem gets much smaller and you should say so and ask for more.

2. "One store, or a chain?" This is the question that produces the modelling insight in finding the objects, below. A chain forces you to separate what a product is from how many of it this store has. Assume a small chain.

3. "Are suppliers and reordering in scope?" Reordering is the natural home for the Observer pattern (low stock triggers a purchase order) and it is a satisfying extension. Assume it is in scope as a notification, with actual purchasing cut.

4. "How does payment work — cash, card, split payment?" Take payment as an interface with a success or failure result. Split payment across two cards is a good extension question and a bad use of the middle of the round.

Assumptions this lesson makes

  • A small chain: several stores, one shared product catalogue.
  • Point of sale: a cart, scanning items, applying prices, taking one payment, printing a receipt.
  • Inventory: per-store stock levels, decremented on sale, with a low-stock threshold.
  • Reordering: a notification when stock falls below a threshold. Placing the actual purchase order is out of scope.
  • Several tills per store operating concurrently against the same stock.

Requirements

Functional requirements

Text
1. Look up a product by barcode and get its current price at this store2. Add and remove items in a cart, including weighed items3. Total the cart, applying any active promotions4. Take payment and produce an order5. Decrement inventory for every item sold6. Raise a low-stock notification when a product falls below its reorder point

Requirement 2 has a hidden detail worth catching: weighed items. Bananas are priced per kilogram, not per unit, so a cart line is not always "quantity 3". Noticing this in the requirements phase is a small, genuine domain signal — it means OrderLine needs a quantity with a unit, not an integer.

The hidden detail in requirement twoOrderLine with an integer• Quantity 3 means three of something• Bananas priced per kilogram break it• The till has to special-case produceOrderLine with a quantity• A number plus a unit: units or kg• Weighed and counted goods look alike• Pricing multiplies without a branch
Noticing weighed goods in the requirements phase is a small, genuine signal of domain attention.

Non-functional requirements

N1. Stock must never go negative. Four tills selling the last two packets of coffee must result in two sales and two refusals, not four sales.

N2. A till must complete a sale even if the inventory service is slow. A supermarket that stops selling because a background system is unhealthy is a business failure. This tension — correctness versus availability — is the honest heart of this problem and Checkout and inventory update, in code handles it.

N3. Adding a promotion type must not modify checkout code.

Explicitly cut, and named

Text
CUT (named, not forgotten)- Loyalty programmes and points- Returns and refunds- Staff scheduling and payroll- Supplier contracts, purchase orders, and goods-in receiving- Shelf planning, waste tracking, refrigeration alarms- Self-checkout hardware, scales, and scanner protocols

Name them as you write them. "Returns I'm cutting — it's a whole second flow with its own states, and it would double the design. Loyalty I'm cutting because it mostly affects pricing, which I'm already putting behind an interface, so it would slot in later without changing the structure."

That second half is the good part: it tells the interviewer that the cut item has a place to go in your design. Cutting something and knowing where it would attach is much stronger than cutting it because it is hard.

The numbers that shape it

A single supermarket, order of magnitude:

  • Catalogue: tens of thousands of products.
  • Tills: eight to twenty, each scanning an item every couple of seconds at peak.
  • Transactions: a few thousand per store per day.

So: catalogue lookups are frequent and must be fast (a hash lookup by barcode, or a cache); writes are moderate; contention is on a few popular products rather than on everything. That last observation matters — it means per-product locking is fine and a global inventory lock is not.

Finding the objects

The candidate nouns

Product, item, price, barcode, store, shelf, stock, cart, basket, customer, checkout, order, line, payment, receipt, supplier, threshold, promotion.

Four things the word "product" is hidingProduct — catalogue entryStockItem — units in storeShelfPrice — per storeOrderLine — what was sold
Price does not live on Product: the same product costs different amounts by store and by week.

The modelling error most attempts make

Almost every first attempt writes this:

Java
public class Product {    private String barcode;    private String name;    private Money price;    private int quantityInStock;      // <-- the error}

One class, holding both what the product is and how many are on the shelf. It works for a single store and breaks immediately for two, because the same tin of tomatoes has one name and two stock levels.

It is worse than it looks even for one store. The catalogue and the stock level have different lifetimes, different owners, and different change rates:

The productThe stock level
Who owns itHead office / the catalogueThe individual store
How often it changesRarely — a name, a descriptionConstantly — every sale
ScopeGlobal across the chainPer store
Who reads itEvery store, the websiteThis store's tills
ConcurrencyRead-mostlyWrite-heavy and contended

Two different things. Split them:

Java
public class Product {                    // the catalogue entry: global, read-mostly    private final String barcode;    private final String name;    private final Category category;    private final UnitOfMeasure unit;     // EACH or KILOGRAM — the weighed-item detail}public class InventoryItem {              // this store's holding of that product    private final Product product;    private final Store store;    private Quantity onHand;    private Quantity reserved;            // in someone's cart at a till right now    private final Quantity reorderPoint;    public Quantity available() { return onHand.minus(reserved); }}

Say the reason out loud, because the sentence is the score: "I'm separating Product from InventoryItem because a product is a chain-wide catalogue entry that rarely changes, and a stock level is per-store and changes on every sale. Merging them makes a second store impossible and puts a read-mostly record on the hottest write path in the system."

If you made this error in your attempt, that diff alone was worth the forty-five minutes. It is the same shape as Seat versus ShowSeat in the movie booking design — a permanent definition separated from a per-context state — and noticing the repeat is exactly the pattern transfer this course is building.

Price does not live on Product either

The same argument applies once more. A price varies by store, by date, and by promotion. Put a Money price field on Product and a chain cannot run a regional price, and a promotion has to mutate the catalogue.

Java
public interface PricingService {    Money priceFor(Product product, Store store, Instant at);}

One interface, and promotions become implementations rather than fields (Extensions).

The rest of the model

ClassIts one-sentence job
ProductThe chain-wide definition of one sellable thing
StoreOne physical location with its own inventory
InventoryItemHow much of one product this store holds
CartThe items a customer is buying, before payment
OrderA completed sale: its lines, its total, its payment
OrderLineOne product, one quantity, and the price charged
PaymentOne attempt to collect the order total
SupplierWho to reorder from; thin, only needed for notifications
PricingServiceTurns a product and a context into a price

Nine, which is at the limit for forty-five minutes — a consequence of the breadth, and another reason the cut list matters.