Case study 02SaaS · Restaurants
Wasl
A multi-tenant restaurant operating system, with a white-label Flutter app for every restaurant.
In production
- Role
- Founder & engineer — product, architecture, backend and Flutter clients
- Stack
- Flutter
- Dart
- Laravel 12
- PHP 8.3
- PostgreSQL (RLS)
- Livewire
- Laravel Reverb
- Firebase Cloud Messaging
- Redis
- Docker
- GitHub Actions
01
The product
Wasl (وَصل) is a restaurant operating system that I designed and built, running in production at wasl.abdelmo3ty.com. A restaurant subscribes and runs its menu, orders, kitchen, tables, invoices, inventory, loyalty, reservations and analytics on one Arabic-first, right-to-left platform.
It has five surfaces. Guests scan a table QR and order without signing in. Waiters, cashiers, the kitchen and the host each get their own screen. Owners have an admin console, the platform team has its own, and each restaurant can have a white-label mobile app.
The backend is a modular monolith of 25 modules on Laravel 12, PHP 8.3 and PostgreSQL 18, and restaurants are kept apart inside the database itself with Row-Level Security. Two Flutter clients sit on top: one engine that becomes each restaurant's branded Android and iOS app, and a Windows client for the cashier and the kitchen display.
02
In action
03
Screens
Screenshots from the product.
04
The challenge
01
Isolation in the database, not by convention
Tenant tables enable and force PostgreSQL Row-Level Security, with a policy keyed on a per-transaction tenant setting that stays safe behind PgBouncer. The app refuses to boot if its database role could bypass RLS, and CI runs the tenancy tests as a non-privileged role.
02
Money that always adds up
Amounts are stored as integer minor units and every price goes through one pricing entry point. Bill splits use a largest-remainder allocator, so the shares always sum to the total; CI property-tests it over 50,000 generated cases. The mobile app never computes a total itself.
03
Real-time floors on unreliable networks
Kitchen, expo, host and tracking screens use private WebSocket channels and fall back to polling. Domain events leave through a transactional outbox. Offline, the kitchen and cashier queue their actions, each with an idempotency key created at click time, so syncing back cannot create duplicates.
04
One Flutter engine, many branded apps
Each restaurant's identity (app name, bundle IDs, deep-link host, colours) is fixed at build time; menu, offers, loyalty and theme load at runtime. The Material 3 theme picks text colours with the WCAG contrast formula, and table-QR links open the installed app through App Links and Universal Links.
05
My role
Founder & engineer — product, architecture, backend and Flutter clients
What I built
- Multi-tenant SaaS with PostgreSQL Row-Level Security
- Table-QR ordering without sign-in
- Real-time kitchen and staff screens over WebSockets (Laravel Reverb)
- White-label Flutter app per restaurant, for Android and iOS
- Windows Flutter client for the cashier and kitchen display, with an offline operation queue
- Money-safe billing and exact bill splits
- Transactional outbox and idempotent APIs
- Arabic-first, right-to-left design with English localisation
- Quality gates: PHPStan level 9, Deptrac layer rules, GitHub Actions CI
06
Architecture
The layers of the system, from what people touch to how it ships. Every item comes from the project's stack.
Clients
- Flutter white-label app · Android & iOS
- Flutter Windows client · cashier & kitchen
- Livewire
Real-time
- Laravel Reverb (WebSockets)
- Polling fallback
- Firebase Cloud Messaging
Backend
- Laravel 12
- PHP 8.3
- Modular monolith · 25 modules
- Transactional outbox
Data
- PostgreSQL (RLS)
- Redis
Delivery
- Docker
- GitHub Actions
- PHPStan level 9
- Deptrac
07
Engineering
Row-Level Security
Restaurants are kept apart inside PostgreSQL itself; the app refuses to boot if its role could bypass RLS.
Money-safe billing
Integer minor units, one pricing entry point and a largest-remainder bill split, property-tested in CI.
Real-time floors
Private WebSocket channels with a polling fallback; domain events leave through a transactional outbox.
Offline queue
The kitchen and cashier queue actions offline with idempotency keys, so syncing back cannot duplicate.
White-label Flutter
One engine becomes each restaurant's branded app: identity at build time, menu and theme at runtime.
Quality gates
PHPStan level 9, Deptrac layer rules and GitHub Actions CI.
08
Product flow
- 01
Table QR
A guest scans the table's QR code.
- 02
Order
They order from the menu without signing in.
- 03
Kitchen
The order reaches the kitchen screen in real time.
- 04
Staff
Waiters, cashiers and the host each work from their own screen.
- 05
Bill
Money-safe billing with exact bill splits.
09
By the numbers
- Backend modules with enforced layer boundaries
- 25
- Product surfaces, from guest QR ordering to a white-label app
- 5
- Backend test files
- 685


