Fooodo architecture
How Fooodo is built — the two-service split of menu app and payment service, the company/restaurant/table multi-tenancy model, the POS-agnostic connector contract, and where UVS sits.
Fooodo runs as two cooperating services plus an external POS, reached through a POS-agnostic connector contract. This page is the system map: how the pieces connect, why they're split, and how multi-tenancy works.
The two services
- Menu app. Customer-facing flow (QR → browse → order), the operator admin panel, and all POS connector traffic.
- Payment service. Separate service behind a token-authenticated API. Owns Mollie integration, webhook routing, donation routing, and tip routing. Has its own internal admin.
- POS connector. Whichever POS the restaurant runs. Fooodo treats it as the system of record for the menu and as the kitchen's source of truth for orders. R-Keeper is the live reference connector today; other POS connectors are scoped per-customer — the connector contract itself is POS-agnostic.
The two Fooodo services talk over an authenticated HTTP API; there is no shared database, no shared queue, and no shared deployment. They can be deployed independently.
Why split the payment service
Three reasons:
- Compliance scope stays small. Card data only ever touches the payment service; the menu app receives a checkout URL and a callback. PCI scope is bounded to one codebase.
- The payment service is reusable. It was designed to serve multiple Fooodo surfaces over time, not just the current menu app. New products consume it without re-implementing payment plumbing.
- Failure isolation. If Mollie has a regional outage, the menu app stays up; orders queue in the "ready to be paid" state and reconcile when the payment service recovers.
Multi-tenancy
Every record in the menu app is scoped through this hierarchy:
- Companytenant boundary · brand · billing
- RestaurantPOS config · hours · default flow
- TableQR code · per-table flow override
- Orderitems · payments · tips · donations
- Company is the tenant boundary. Currently one active company in production (Čili Pizza), but tenant separation is enforced everywhere — adding a second chain doesn't require a separate deployment.
- Restaurant admins can only manage their own restaurant — the role system enforces this at the policy layer.
- Every QR code resolves to a specific table at a specific restaurant. There is no shared "scan to order" code; Fooodo always knows which seat an order is for. This is what makes Pay-Later, table-side bill split, and seat-level analytics possible.
Where UVS sits
UVS is the order and operations core inside the menu app — the central event stream that records everything the platform writes (orders, payments, table state, kitchen tickets) before propagating to consumers (the active POS connector, the payment service, future modules).
For integrators this matters concretely: modules don't talk to each other directly, and the contract is POS-agnostic. A connector for a new POS doesn't touch the kitchen flow or the payment service — it speaks the UVS contract, and inherits payments, multi-tenancy, and reporting for free. R-Keeper is one implementation of that contract; the contract itself is the system. The contract is documented in the integration repo, which partners receive during onboarding.
Stack at a glance
| Component | Shape |
|---|---|
| Menu app | PHP backend, React frontend, PostgreSQL, Redis-backed job queue |
| Payment service | Independent PHP service with its own database and admin |
| Payment provider | Mollie (Card, Apple Pay, Google Pay) |
| POS connector | R-Keeper (live); other POS connectors scoped per-customer |
| Marketing site | Next.js + next-intl + Fumadocs (this site) |
Specific framework versions are intentionally omitted from this public surface — partners building POS connectors get the version pinning during onboarding alongside the integration repo.
What lives where
- Menu management, orders, tables, customer-facing flow → menu app.
- Card data, refunds, payment methods, tips, donations → payment service.
- Menu source of truth, kitchen tickets, reports → your POS (R-Keeper today, other connectors as they ship).
- Marketing copy, public docs, AI assistant, MCP server → this site.
If you are integrating with Fooodo, the API surface you care about is the menu app's external API. The payment service is internal — the menu app proxies to it.
Getting started with Fooodo
The hands-on onboarding path from "we just signed" to live orders at your first location — menu sync, QR tables, the dry run, and what to watch in week one.
Order flows — Pay-First vs Pay-Later
When to use Pay-First versus Pay-Later, how each behaves end-to-end, the order states operators see in the admin, and the background jobs that keep orders in sync with the POS.