The software a restaurant actually runs on: take orders at the table, fire tickets to the kitchen, bill with GST, track stock, cost recipes to the gram, purchase from vendors, read the analytics, and run its presence on the delivery platform without leaving the till. An async FastAPI modular monolith over PostgreSQL — twelve domain packages, twenty-five migrations, self-hosted authentication, and a tenancy model where the restaurant is the boundary.
A restaurant operating system — POS and GST billing, inventory, recipe costing, procurement and tiered analytics on one multi-tenant core.
A modular monolith, not microservices
Twelve domain packages sit inside one deployment against one database, each with the same internal shape — models, schemas, service, router — and cross-domain access only ever through the service layer, never a model import in a router. The boundaries are real, but recipe costing can still read ingredient prices without a network hop or an eventual-consistency story to explain to anybody.
Tenancy that cannot be passed in
A memberships table is the authorization source of truth, and the active restaurant is resolved from it on every request. The identifier is never accepted as a query parameter or a body field anywhere in the application, and a test greps the call sites to fail the suite if that ever regresses. A tenant boundary the caller gets to name is not a boundary, it is a convention.
Money is recomputed, never received
Subtotal, discount, GST and total are rebuilt from the line items on every mutation; the front end never computes a figure. GST is applied after the discount, which is the correct order for Indian tax treatment, and the rate is snapshotted onto the order at creation so changing a restaurant’s rate can never retroactively rewrite a historical bill.
The draft-to-ticket immutability boundary
Items are added as drafts — billable immediately, freely editable. Firing a kitchen ticket assigns them and makes them immutable; from then on they can only be cancelled, with a reason. Checkout is rejected server-side if any active item is still an unfired draft, so a customer can never be billed for a dish the kitchen was never told to make.
Stock that moves without racing
A stock adjustment is a single arithmetic update under the row’s write lock rather than a read-modify-write, and a negative result is a typed validation error rather than a 500. The weekly stock log posts as one transaction that rolls back entirely on any failure — replacing a per-row loop where a mid-way error left earlier rows committed and a retry double-counted. Ticket-driven deduction locks its ingredient rows and clamps at zero, because a bookkeeping shortfall must never block a kitchen that already holds the physical ticket.
Authentication built from primitives
No external identity provider: bcrypt password hashing with a pre-hash to clear the algorithm’s length cap, stateless short-lived access tokens verified by signature alone, and opaque refresh tokens stored hashed, rotated single-use, and revoked on logout, deactivation or reuse. Authorization is never trusted from the token — the user is re-loaded and the tenant scope re-resolved on every single request. There is no self-signup at all.
POS and billing
Table-based or straight billing, kitchen tickets, discounts and extra charges, cash or UPI checkout, and a bill designer with a live preview.
Inventory
Race-free stock movement, atomic batch adjustment, append-only price history, and a deletion-impact call that shows the real blast radius before you confirm.
Recipe costing
Snapshot and live cost shown side by side with a divergence banner, over a unit-conversion layer shared with ingredients so the two cannot drift.
Procurement
Vendors, ingredient offers with exactly one current supplier per ingredient, and purchase orders that credit stock on receipt.
Analytics
Fifteen endpoints aggregated live at request time — no rollup tables, no staleness — behind a tier gate that maps every widget through one config function.
Rider workspace
A mobile-first surface scoped to the rider’s own deliveries with no tamperable input, carrying handover OTP verification and doorstep QR collection.
Status: Live in production
Shape: Modular monolith
Backend tests: 299
Divyakush Punjabi