A menu looks like a list of items with prices. It is actually a graph: items own modifier groups, modifier groups own options, and options can open their own nested groups. A burger is simple until someone wants it as a combo, with a side swap, no pickles, and extra sauce that costs fifty cents.
The first schema
We started with a flat table of items and a JSON blob of options. It shipped fast and broke the first time a restaurant asked for a half-and-half pizza. Pricing rules lived in the blob, so every client had to reimplement them.
- Options could not be shared between items.
- Price changes required editing every item that used an option.
- Reporting could not tell a modifier from a line item.
What we run today
Modifier groups are first-class records with min and max selection rules, and items reference them. Pricing is resolved server-side from the same rules the POS uses, so the cart total and the kitchen ticket always agree.
If two systems calculate the same price, one of them is wrong.
The graph is harder to query, but it is the only model that survived contact with real menus.