Problem statement
Design the object model and core APIs for the software that drives an automatic beverage machine. The machine has several dispensing outlets and a shared ingredient store; the object model owns the recipes, the inventory, and the logic that turns a drink request into a served cup or a clear refusal. Whatever sits on top (a keypad, a touch panel, a test harness) merely selects a beverage and reads the result back.
Operating context. One physical machine with a fixed number of outlets (say 2–4) that can each be dispensing a different drink at the same moment. Every beverage is defined by a recipe: a set of ingredient-to-quantity requirements (water, milk, coffee syrup, tea leaves, sugar, and so on) drawn from one shared, refillable inventory. A request names a beverage and an outlet; the machine may serve it only if every required ingredient is currently in stock in sufficient quantity, and serving atomically deducts those amounts. When an ingredient runs low or dries up, requests that need it are refused with the specific missing items named, while drinks that do not need it keep flowing. An operator can top up any ingredient at any time. There is no network and no persistence — a single in-process engine driven by concurrent callers on multiple threads.
Out of scope. The physical hardware and actuator/valve control, the keypad or touchscreen UI, cash or cashless payment, remote telemetry or cloud reporting, and any persistence or save/restore across a power cycle. Design only the in-process engine and its object model, not the electronics or transport around it.
What to produce. The class hierarchy (entities such as Machine, Outlet, Beverage/Recipe, Ingredient, Inventory, and any value objects like a quantity or an ingredient key), the public API each class exposes, and the state transitions for an outlet (idle → dispensing → idle) and for a request (accepted → served, or rejected). Be explicit about: how a recipe checks and consumes ingredients atomically, how two outlets dispensing at once never over-draw the shared inventory, how a low or empty ingredient is detected and reported with the exact missing items, how refills interleave safely with dispensing, and how a brand-new beverage is added without editing existing beverages or the dispensing core.