Skip to content
All work

Case study 02SaaS · Restaurants

Wasl

A multi-tenant restaurant operating system, with a white-label Flutter app for every restaurant.

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
KitchenLiveNewTableTableCookingTableTableTableReadyTableMenuTABLEOrderTABLE QR
Illustration

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

A real order on Wasl's demo restaurant, recorded from the running system. A guest at table 9 orders from their phone; the grill station's kitchen display receives the ticket, the cook acknowledges the guests' allergies, starts it and marks it ready, and the guest's phone follows each step. The ordering part plays at 1.35× speed; the rest is real time.

03

Screens

Screenshots from the product.

  • Wasl, screen 1
  • Wasl, screen 2
  • Wasl, screen 3
  • Wasl, screen 4

04

The challenge

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  1. Clients

    • Flutter white-label app · Android & iOS
    • Flutter Windows client · cashier & kitchen
    • Livewire
  2. Real-time

    • Laravel Reverb (WebSockets)
    • Polling fallback
    • Firebase Cloud Messaging
  3. Backend

    • Laravel 12
    • PHP 8.3
    • Modular monolith · 25 modules
    • Transactional outbox
  4. Data

    • PostgreSQL (RLS)
    • Redis
  5. 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

  1. 01

    Table QR

    A guest scans the table's QR code.

  2. 02

    Order

    They order from the menu without signing in.

  3. 03

    Kitchen

    The order reaches the kitchen screen in real time.

  4. 04

    Staff

    Waiters, cashiers and the host each work from their own screen.

  5. 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