HomeAboutProjectsExperienceSkillsEducationContact
← Back to Projects
Multi-tenant restaurant OS

A restaurant operating system running on three surfaces at once — a Flutter PoS on Windows terminals and Android handhelds, a white-label Next.js storefront per tenant, and TypeScript microservices behind an nginx gateway on Cloud Run.

0

Surfaces

0

Microservices

0

REST endpoints

0

RBAC roles

FlutterTypeScriptMongoDBGCP Cloud RunStripe ConnectGemini Vision
View Demo + Docs

=== RESTARAGE POS ===

ROLE: Full Stack Developer & Team Lead

DURATION: Dec 2024 — July 2026

SURFACES: Windows PoS · Android Handheld · Customer PWA

STACK: Flutter · TypeScript · MongoDB · GCP · Stripe

SCOPE: Multi-tenant restaurant operating system

*** THANK YOU ***

preparing

DL-00018

Duo Bucket Meal

$182.40

completed

T-00042

Tandori Pan Pizza

$23.63

Flutter
TypeScript
Node.js
MongoDB
GCP Cloud Run
Stripe
Firebase
Next.js
GraphQL
Flutter
TypeScript
Node.js
MongoDB
GCP Cloud Run
Stripe
Firebase
Next.js
GraphQL

See It In Action

The PoS experience on a handheld Android terminal — vertical capture, real workflows.

View Demo + Docs

demo video · overview doc · SRS

Three Surfaces, One Platform

Windows checkout terminals, Android handhelds, and customer-facing storefronts — one codebase, three experiences.

Windows PoS

Windows PoS

Primary checkout terminal — persistent side navigation, thermal receipt printing over USB/TCP, and full menu/order/billing workflows.

USB + TCP printers22+ operator screensGetX state layer

Gateway Routing

pos.restarage.com — custom nginx on Cloud Run, path-based routing to three microservices. Hover a route to trace the request.

Flutter PoS
Next.js PWA

pos.restarage.com

nginx gateway :8080

iam-core:3001

Users, restaurants, branches, subscriptions, Firestore notifications, Stripe Connect

68 REST · 73 GraphQL
menu:3003

Catalog, inventory, AI extraction, menu versioning, public PWA reads

19 REST · 51 GraphQL
tps-service:3002

Orders, transactions, PendingCheckout, Stripe web-order webhooks, analytics

32 REST · 5 GraphQL
MongoDBFirestoreStripe

Architecture Evolution: 7 → 3 Services

Legacy Topology

usersrestaurantsorderstransactionsnotificationscomplaintsmenu
NATS StreamingRabbitMQ

Current Topology

iam-coremenutps-service
HTTPFirestore

Retired message brokers to eliminate persistent-connection scaling problems on stateless Cloud Run containers. Cross-service calls are fire-and-forget HTTP; real-time delivery uses Firestore snapshot listeners.

Pay-First Order Lifecycle

A web order traced from checkout tap to kitchen board — no order exists until Stripe confirms payment.

1

Customer taps checkout

PWA cart → Next.js BFF route

2

PendingCheckout snapshot

Full cart stored in tps-service — 24h TTL, no Order yet

status: pending
3

Stripe Hosted Checkout

Connect Express destination charge · 2.5% platform fee

4

checkout.session.completed

tps-service webhook — filtered by metadata.type === 'pwa_checkout'

5

Order created · Paid

fulfillPendingCheckout → kitchen queue

6

notifications/internal/dispatch

order.placed → iam-core recipient resolver

7

Firestore inbox write

users/{uid}/notifications — batch write per role

8

Flutter PoS listener

Snapshot → ValueNotifier → order board updates

“No kitchen order exists until Stripe confirms payment.”

AI Menu Onboarding

From paper pamphlet to structured POS catalog — vision-LLM extraction with strict draft isolation and human-in-the-loop review.

📤

Batch Upload

≤20 files · ≤25 MB each · JPEG/PNG/WebP/HEIC/PDF

☁️

GCS Staging

restaurants/{id}/menu-sources/{draftId}/… · 30-day lifecycle

👁️

Vision-LLM

Gemini via OpenRouter · 1–2 images/chunk · PDFs separate

📋

Consolidate JSON

Markdown per chunk → merged structure

Schema Map → Draft

Categories · Meals · Toppings · Sizes — progressive persist

DRAFT

AI writes here only

Progressive schema mapping into categories, meals, toppings, and sizes. Review markers flag uncertain entities.

newneeds reviewclean
🔒

Invariant gate — AI never touches Live

LIVE

Owner promotes explicitly

Exactly one LIVE version per restaurant. Make Live triggers cart-safety signal across POS and PWA.

“The AI extraction pipeline shall never read or mutate the Live menu version.” — Bias: false needs review over silent wrong price.

Cart Safety

Make Live clears every active cart across POS and PWA — with a casual banner and triple-path detection.

Menu Version

DRAFT

AI import 2026-07-29

Flutter PoS Cart

Mingels Bucket$249.00
Leg Pieces ×2$18.00

PWA Cart

Mingels Bucket$249.00
Leg Pieces ×2$18.00
restaurants/{id}/menuState/current
Firestore realtime

Snapshot on restaurants/{id}/menuState/current

Polling fallback

GET /menu/versions/live if realtime misses

Reconnect reconciliation

Detect version change before checkout on reconnect

Target: 100% cart-safety correctness in pilot QA

The Cloud Run Race Condition

When NATS delivered owner:created to every replica simultaneously, three Cloud Run instances raced to create the same restaurant owner.

Three replicas receive owner:created via NATS at-least-once delivery. All converge on one MongoDB write.

Click a replica to run the race

try {
  await user.save();
} catch (saveError) {
  if (saveError.code === 11000) {
    console.log("Duplicate detected. Another replica created the owner first.");
  }
}

Mitigation Ladder

1
Dup-key handlingshipped

Graceful E11000 catch on owner creation

2
Compound partial indexrecommended

(restaurant + role) unique for Owner

3
Redis distributed lockplanned

SET key locked PX ttl NX

4
NATS JetStreamfuture

Exactly-once delivery

Flutter PoS Internals

A four-layer GetX architecture — presentation, reactive state, data access, and Hive persistence.

1

Presentation

lib/components/

Pages, dialogs, widgets — role-filtered navigation shell

HomePage nav catalogOrder/checkout flowsMenu admin UIshowcaseview onboarding
2

Reactive State

lib/controllers/

GetX controllers — screen state, orchestration, realtime bridges

PrinterControllerPayment controllersValueNotifier notifications bridge
3

Data Access

lib/api/

CrudApi transport + 11 domain API clients + adapters

CrudApi auth headersDataRepository cache-firstGraphQL-style payloads
4

Persistence

lib/db/

Hive local_db box — 5-min freshness, user-scoped cache keys

CachedDataAdapterPrinter configJWT in SharedPreferences

Cache-First Reads

DataRepository.get hashes endpoint + port + body + user email → Hive lookup with 5-minute freshness window.

→ Fresh cache: return immediately

→ Stale: background refresh via retry callback

→ Writes: bypass cache, straight to API

Split Realtime Channels

FirestoreNotification inbox — snapshot listeners
Socket.IOStripe payment-success — API verify fallback

Emulator Routing

Android emulator rewrites localhost 10.0.2.2 automatically in CrudApi.

Thermal Printer Transports

TransportAndroidiOSWindows
USB
Bluetooth Classic
BLE
TCP/Network

Fork-maintained flutter_pos_printer_platform_image_3 — resolved image v3/v4 dependency conflict after upstream discontinuation.

More From the Product

Branch management, billing, and the add-on marketplace — real screenshots from the live PoS.

Branch management dashboard

Branches

Subscription and wallet billing

Billing

Store marketplace for add-on services

Store

Customer Storefront

White-label PWA at {slug}.order.restarage.com — host-header tenancy, Stripe Connect, and real-time POS sync from a single Next.js build.

https://kfc.order.restarage.com
slug: kfc

Resolved from Host header

Caching Ladder

1Store page ISR30s
2Restaurant config60s
3Menu API max-age30s
4React Query staleTime60s
5Service Worker APINetworkFirst 10s / 24h cache

Core Web Vitals Targets

LCP< 2.5s

Stretch: < 1.8s

INP< 200ms

Stretch: < 100ms

CLS< 0.1

Stretch: < 0.05

TTFB< 800ms

Stretch: < 400ms

Honest note: menu route is not SSR'd yet — client fetch with skeleton. Tiered remediation roadmap documented.

By The Numbers

The engineering footprint — counted up on scroll, printed on thermal paper.

=== ENGINEERING RECEIPT ===

REST endpoints0
GraphQL operations0
MongoDB collections~0
RBAC roles0
Active microservices0
Notification events0
Max upload files0
Max file size (MB)0
GCS retention (days)0
Extract job timeout (min)0
Platform fee (%)0.0
Checkout TTL (hours)0
Cache freshness (min)0
TOTAL SURFACES3
TOTAL SERVICES3 active (+ 7 legacy)

*** END OF RECEIPT ***

Restarage v6 — The AI Vision

From reactive legacy PoS to a proactive AI-driven restaurant operating system.