Architecture
The big picture of the Ghost Kitchen system: what it is, what it consists of, and why this stack was chosen.
:::info Who this section is for "Overview", "Roles & panels" and "Launching a new brand" are written in plain language — they can be shown to the client and the agency. "Data model" and "Integrations" in the Backend section are the technical area of the backend partner. :::
What we are building
Ghost Kitchen is a custom multi-tenantMulti-tenantAn architecture where a single application instance and one database serve many isolated customers (tenants). Here a tenant equals a brand. platform for managing a network of delivery restaurants and brands. It replaces the mix of aggregatorsAggregatorA marketplace platform that collects orders for restaurants (e.g. a food-delivery app), routes them to the kitchen and takes a commission. (Uber Eats, Lieferando, Bolt Food) and renting Shopify with one codebase and one database that serve all brands at once.
The platform provides: an order storefront, order intake and payment, menu management, routing orders to the kitchen (POSPOSPoint of Sale — a restaurant's cash-register system. Here it is Liefersoft: it receives orders from the platform, prints receipts and handles fiscal accounting.), analytics and brand management — at a lower commission than the aggregators.
Why not Shopify
A detailed breakdown is in the client materials. In short, Shopify licenses each store separately: at 10 brands that's 10 subscriptions, 10 admin panels, 10 integrations and customer data siloed from each other. The hardest part — the link to the kitchen register — would still have to be hand-built. A custom multi-tenantMulti-tenantAn architecture where a single application instance and one database serve many isolated customers (tenants). Here a tenant equals a brand. stack removes this linear cost: a new brand is rows in the database, not a new subscription.
Recommended stack
| Layer | Technology | Why |
|---|---|---|
| Customer storefront | Nuxt 3NuxtA meta-framework on top of Vue.js. Supports server-side rendering (SSR) and SPA mode. Used for the customer storefront and the admin panels. (Vue) in SSRSSRServer-Side Rendering — the page is built on the server and delivered as ready HTML. Needed for SEO and fast first load of the storefront. mode | SEO and speed for food sites |
| Admin panels | Nuxt / Vue in SPASPASingle-Page Application — the app renders in the browser without page reloads. Suitable for behind-login admin panels where SEO is not needed. mode | Behind login, no SEO needed |
| Data + API + admin core | DirectusDirectusAn open-source headless platform on top of PostgreSQL: provides the data model, REST/GraphQL API, authentication, RBAC and a ready-made admin UI without writing code. | Data model, REST/GraphQL, RBACRBACRole-Based Access Control — access management based on roles. Permissions are granted to a role (Company Admin, Brand Admin, Restaurant Manager) rather than to each user individually., ready admin UI |
| Business logic & integrations | Node adapter | Orders, webhooks, POS/payment link |
| Database | PostgreSQLTenantA logically isolated customer in a multi-tenant system. In Ghost Kitchen a tenant is a brand: its data is separated from others by `brand_id` and access rules. | Single source of truth |
| Payments | MollieMollieA European payment provider. Accepts payments (cards, iDEAL, Apple/Google Pay, etc.) and reports payment status via webhooks. | Payment acceptance (EU) |
| Restaurant register | LiefersoftLiefersoftA restaurant POS / cash-register system (Germany) with TSE fiscal compliance. The platform pushes orders to it for preparation and fiscal accounting. | Preparation + TSE fiscalizationTSE fiscalizationGerman technical requirements (Technische Sicherheitseinrichtung) for cash registers: every sale is securely signed. Handled on the POS side (Liefersoft). |
| Hosting | Vercel + Neon + Hetzner | Flat cost, does not grow with brand count |
:::note Answering "SPA or not?" The customer storefront is SSR, not SPA. Search ranking and link previews (Google, messengers) are critical for delivery conversion. Admin panels are SPA: they are behind login, there's nothing to index, and interactivity matters more. Nuxt supports both modes in a single framework. :::
System map (C4 — context)
Three backend layers
The backend is deliberately split into three parts with clear boundaries:
- DirectusDirectusAn open-source headless platform on top of PostgreSQL: provides the data model, REST/GraphQL API, authentication, RBAC and a ready-made admin UI without writing code. — "80% without code". We describe the data model (brands, kitchens, menu, orders, users) and immediately get a REST/GraphQL API, authentication, RBACRBACRole-Based Access Control — access management based on roles. Permissions are granted to a role (Company Admin, Brand Admin, Restaurant Manager) rather than to each user individually. and a ready admin UI for content operations.
- Node adapter — "the remaining 20%". What Directus does not do: checkoutCheckoutOrder checkout: entering contacts and address, choosing time/delivery, payment and confirmation. In the custom stack it is fully under our control. orchestration, receiving Mollie webhooksWebhookAn HTTP notification an external service (e.g. Mollie) sends to the platform on an event — for example, "payment paid". An alternative to constantly polling the API., pushing the order to LiefersoftLiefersoftA restaurant POS / cash-register system (Germany) with TSE fiscal compliance. The platform pushes orders to it for preparation and fiscal accounting., retries, idempotencyIdempotencyThe property of an operation yielding the same result when repeated. Protects against duplicate orders/payments when a webhook is redelivered or a request is retried., dead-letter queueDead-letter queueA queue for messages/tasks that could not be processed after all retries. They are handled manually so that no order is lost when an integration fails..
- PostgreSQLTenantA logically isolated customer in a multi-tenant system. In Ghost Kitchen a tenant is a brand: its data is separated from others by `brand_id` and access rules. — single source of truth. All brands in one database,
isolated by
brand_idand access rules.
Multi-tenant: tenant = brand
Each brandBrandA trade brand inside the platform (e.g. "Pizza Burger"). One brand can have several restaurants/kitchens. In the architecture a brand equals a tenant. is a logically isolated tenantTenantA logically isolated customer in a multi-tenant system. In Ghost Kitchen a tenant is a brand: its data is separated from others by `brand_id` and access rules. in a shared database. One brand's data is invisible to another, but the platform (Company AdminCompany AdminThe platform owner. Sees and manages all brands, restaurants, orders and cross-brand analytics; creates new brands.) sees everything at once — hence cross-brand analytics "in one SQL query", impossible on Shopify's siloed stores.
Scale
Right now the conversation with the client is about 3–10 brands over a two-year horizon. But the multi-tenant architecture is designed from the start to grow to dozens and hundreds of brands without a rewrite: adding a brand requires no deploy, and load is distributed at the level of a single database and one set of services. Concrete horizontal-scaling steps (read replicas, cache, queues) are described in the Backend section as growth demands.
What's next
- Roles & panels — who sees and manages what.
- Order lifecycle — the order's path from click to kitchen.
- Launching a new brand — how a brand appears "in a few clicks".
- Backend and Frontend — technical details.