Go · real time
A restaurant backend without an ORM
Floor and kitchen on the same order state, with hand-written SQL and no polling.
- Period
- 2025 —
- Role
- Design and implementation
- Stack
- Go · Gin · sqlx · PostgreSQL · PL/pgSQL · WebSockets · React 19 · Vite
01 Context
Mozaico runs the floor, the kitchen and the till of a restaurant. An order changes state many times in a few minutes — taken, fired, ready, served, charged — and every one of those changes has to show up on a different screen, in a different room, without anyone reloading anything.
02 Problem
The default pattern — an ORM plus polling — fails at both ends. The ORM hides the cost of the query exactly where the cost matters, and polling every few seconds multiplies load without improving how fast the kitchen actually finds out.
03 Constraints
- An order state cannot land half-written. It goes in whole or not at all.
- Kitchen screens are modest devices, often on shared Wi-Fi.
- Floor and kitchen cannot diverge, not even for a second: that is a wrong dish.
04 Decisions
01
sqlx over hand-written SQL
The SQL sits next to the query it serves, so what you read is what runs. On the hot paths that is the difference between tuning a query and guessing at a generated one.
DiscardedA full ORM. It buys speed on the first CRUD screen and charges for it on every query that matters afterwards.
02
PL/pgSQL for what must not be split
The operations that have to be atomic live as functions in PostgreSQL, next to the data, instead of as a transaction stitched together in application code.
03
WebSockets instead of polling
State travels to the floor and kitchen screens over gorilla/websocket, so a dish marked ready appears where it is needed without a reload and without a poll.
DiscardedPolling every few seconds: more load, more battery on the tablets, and still a delay the kitchen can feel.
- Order changesfloor tablet
- Go · sqlxhand-written SQL
- PL/pgSQLatomic, next to the data
- WebSocketkitchen sees it, no poll
05 Result
- It is the repository with the most commits I have written.
- A dish marked ready shows up where it is needed with no reload and no polling.
- No latency numbers here on purpose: the system is in production but not instrumented for it, and a figure I cannot explain how I measured is worse than no figure at all.
06 Proof
Everything above is visible here: mozaicopro.djasoft.net.pe ↗