An Android ordering system for a burger restaurant, specified end to end for a first-year Software Engineering unit. Three actors — customer, kitchen, floor staff — twelve use cases with a main and an alternative flow each, and a full screen set for all three roles. No code was ever written. The specification was the deliverable.
The class that isn’t there
Menu holds Estado do Pedido(), and connects to nothing.The class diagram has a Utilizador base class, three role subclasses that inherit from it, and a Menu sitting on its own with no association to anything. It has no Pedido. No Mesa, no Refeição either. A system whose entire reason to exist is moving an order from a table to a kitchen and back never modelled the order as an object — order state ended up as a method hanging off the menu.
What did get modelled, in detail, was the interface: every screen, every state, every error message, for all three roles. So the split is not carelessness, it is attention going where the work is visible. We specified thoroughly what the user would see and barely specified what the system would have to hold.
The same instinct shows up in the estimated frequency column of the use case scenarios. All twelve carry 36,500 a year — including create account, which happens once per user. The field was filled because the template had a field, not because anyone estimated anything.
What survives: the unhappy paths
The part that survives all of that is the alternative flows. Every use case has an unhappy path, and those paths were carried through into the screens rather than stopping at the diagram — table unavailable, account already exists, password rejected, quantity out of stock. Most specification at this level draws the happy path and stops. Six years later, that is still the habit from this project I would keep.
Its companion piece is the Banking Management System, from the same year and the same course: there, a model half-built; here, a model never built at all.