RestaurantOS
Multi-branch restaurant mobile app — menus, table booking, scheduling, and live brand presence in one experience
The Context
Most restaurant apps are either glorified PDFs or third-party aggregators that strip the brand identity entirely. A restaurant group with multiple branches has a different problem — each branch has its own menu variations, table layouts, peak hours, and booking capacity, but they all need to live under one unified brand experience. This app was built to give the restaurant full ownership: customers open one app, pick their branch, browse that branch's live menu, book a specific table at a specific time, and receive confirmations — while the operations team manages everything from a single backend that keeps each branch's data cleanly isolated.
Architecture & Execution
The API is organized around a branch-scoped context — every request carries a branch identifier, and the data layer enforces strict isolation so menus, tables, availability windows, and booking records never bleed across locations. Each branch maintains its own menu catalog with categories, items, pricing, and availability toggles that the ops team can update live. The table booking engine models each branch's floor layout as a set of bookable units with capacity and time-slot constraints. When a customer requests a reservation, the engine checks real-time availability from a Redis cache (refreshed on every booking mutation), confirms the slot, and writes the reservation to SQL Server. Firebase handles push notifications for booking confirmations, reminders, and cancellations. The advertisements and announcements layer is a lightweight CMS-style feed — branch managers publish offers and promotions that surface immediately in the app without a release cycle.
Post-Mortem Lessons
Multi-branch data isolation sounds like a schema decision until you're debugging why branch A's menu showed up in branch B's app. Enforcing branch context at the API middleware level — not just in queries — was the only way to make it airtight.
Table availability is a classic double-booking problem in disguise. Optimistic locking on reservation slots with a short Redis TTL on pending holds prevented race conditions during peak traffic without adding noticeable latency.
Live menu updates feel like a small feature until the ops team starts using them at 2pm on a Friday. Designing the cache invalidation strategy around menu mutations — not just TTL expiry — was what made instant updates actually instant.
Customer App (Flutter)
↓
ASP.NET Core API
↙ ↓ ↘
Branch Menu Booking
Context Service Engine
↓ ↓ ↓
Tables Live Schedule
& Caps Offers & Confirm
↓
Firebase (push)
Redis (availability cache)
SQL Server (source of truth)