Zum Inhalt springen

Fallstudie

Betriebs-App Gastronomie

Eine App, die Dienstplan, Lager und die Bestellung bei sechs Lieferanten in einem Restaurantbetrieb zusammenführt.

Jahr
seit 2025
Status
live
Rolle
Architekt & Umsetzer
Stack
React Native, Expo SDK 54, Supabase, TypeScript, Playwright

Das Problem

Ein Restaurant plante Dienste, bestellte Ware und pflegte seine Speisekarte von Hand. Der Dienstplan kostete jede Woche rund zwei Stunden Abstimmung per WhatsApp, Bestellungen bei Lieferanten gingen unter, und weil die öffentliche Website der echten Karte hinterherhinkte, kamen Gäste mit veralteten Angaben an. Reservierungen standen auf Zetteln, von denen gelegentlich einer verschwand.

Dazu kam eine blinde Stelle: Wie ein Service tatsächlich lief, konnte niemand sehen. Wie lange ein Tisch auf die Getränke wartete und wie lange aufs Essen, wusste keiner, und damit fehlte auch jeder Ansatzpunkt, um daran etwas zu verbessern.

Beteiligt waren rund zwanzig Mitarbeitende und die Inhaber, die Gäste, die buchen und essen, und sechs Großhändler mit ständig wechselnden Katalogen und Preisen, von denen keiner eine brauchbare Schnittstelle anbot. Zu bauen war ein System, dem alle gleichzeitig vertrauen.

Die Lösung

Eine App für Inhaber, Personal und Service, darunter eine einzige gemeinsame Datenbank. Dienstplan, Lager, die Bestellung bei sechs Lieferanten und die öffentliche Website lesen und schreiben dieselben Daten, sodass eine geänderte Karte oder eine Reservierung überall zugleich ankommt. NFC-Tags an den Tischen halten jeden Schritt im Service mit Zeitstempel fest.

Auf einen Blick: Personal, Service, die öffentliche Website, NFC-Taps und sechs Lieferanten speisen dieselbe Datenbank, die Dienstplan, Bestellung und den laufenden Betrieb steuert.
Auf einen Blick

Vorher → nachher

Dienstplan pro Woche
Vorher~2 h Hin und Her per WhatsApp
Nachher~10 Min. in der App
Reservierungen
Vorherhandschriftliche Notizen, gingen teils verloren
Nachhereine Live-Übersicht für das ganze Team
Lager und Bestellung
VorherChefsache, Bestellungen gingen unter
Nachherdas Team pflegt es in der App, Bestellungen entstehen automatisch
Tisch-Zeiten
Vorherim Service nicht sichtbar
Nachherpro Tisch erfasst: Zeit bis zum Getränk, Zeit bis zum Essen
Gästefeedback
Vorherauf Papier gab es keinen Kanal
NachherRückmeldung über den Reservierungskontakt
Auskunft zu Gerichten
VorherNachfrage in der Küche mitten im Service
Nachhersteht im Service in der App
Menü-Übersetzung
Vorher~200 € pro Karte, extern vergeben
NachherCent pro Lauf, Sprachmodell in der App
Saisonale Kartenwechsel
Vorhertagelange Änderungen in mehreren Systemen
Nachhereine Aktion in der App

~2 h → ~10 Min.

Dienstplan pro Woche

~200 € → Cent

Menü-Übersetzung

seit Dez. 2025

im Einsatz

Das Ergebnis

Im Einsatz ist das System seit Dezember 2025, und es kommen weiter Funktionen dazu. Die Ergebnisse stehen bewusst einzeln und bleiben auf das begrenzt, was sie tatsächlich verändert haben, statt zu einer Zahl verrechnet zu werden.

Der Dienstplan kostet statt rund zwei Stunden Abstimmung noch etwa zehn Minuten pro Woche in der App. Lager und Bestellung waren früher ein paar Stunden aus der Woche des Inhabers und liegen heute beim Personal, das sie direkt in der App führt, was ein Zugewinn an Delegation ist und keine gemessene Zahl.

Der saisonale Kartenwechsel, dreimal im Jahr, brauchte Tage an Änderungen in Website, Rezeptmengen und Lieferantenverknüpfungen und ist heute eine einzige Aktion. Die Übersetzung der Karte kostet Cent statt rund 200 € pro Karte. Reservierungen laufen digital und gehen nicht mehr verloren, und über die hinterlegten Kontaktdaten gibt es erstmals eine Rückmeldung von Gästen.

Die NFC-Schicht eröffnet etwas, das der Betrieb nie hatte: Weil jeder Tap einen Zeitstempel trägt, sind die Zeiten im Service erstmals messbar, bis zum Getränk, bis zum Essen, bis der Tisch wieder frei ist. Zahlen behaupte ich hier noch keine, die Messbarkeit selbst ist das Ergebnis.

Geplant, noch nicht ausgeliefert: Das Signal „Tisch ist frei" soll die Reservierungskapazität auf der Website speisen, damit die Online-Buchung zeigt, was im Raum wirklich verfügbar ist. Die Daten laufen schon über den Bus, es fehlt die Anbindung an die öffentliche Buchung.

Die Technik dahinterAufklappen · ~4 Min.

Die Architektur

Oben steht eine plattformübergreifende App: im Browser für die Inhaber, auf dem Telefon für das Personal, dazu eine eigene Ansicht für den Service. Darunter liegt eine einzige verwaltete Postgres-Datenbank mit Row-Level Security und Realtime, aus der auch die öffentliche Website liest, die serverseitig gerendert wird.

Alle Oberflächen der Anwendung, die öffentliche Website, die Personal-App, die Service-Ansicht und die NFC-Taps am Tisch, schreiben in eine gemeinsame Postgres-Datenbank mit Row-Level Security und Realtime. Ein Eintrag in einer Zeile löst gestufte Cache-Updates und Bestätigungen per E-Mail und Push aus.
Alle Oberflächen der Anwendung, die öffentliche Website, die Personal-App, die Service-Ansicht und die NFC-Taps am Tisch, schreiben in eine gemeinsame Postgres-Datenbank mit Row-Level Security und Realtime. Ein Eintrag in einer Zeile löst gestufte Cache-Updates und Bestätigungen per E-Mail und Push aus.

Zwischengespeichert wird nach Änderungsrate, wobei ein Adapter Browser- und Gerätespeicher kapselt: Live-Daten aus dem Service laufen im Sekundentakt nach, Speisekarten in Tagen. Weil Website und App auf denselben row insert reagieren, löst eine Reservierung genau eine Bestätigung aus, egal wo sie eingetragen wurde.

Die Lieferanten waren der schwierigste Teil. Keiner der sechs bot eine Schnittstelle an, weshalb ich bei einem die Bestellendpunkte des Webshops so lange rückentwickelt habe, bis sich darüber programmatisch bestellen ließ; ein zweiter lieferte immerhin einen halbstrukturierten JSON-Feed, und beim dritten blieb nur der Katalog, ausgelesen mit Playwright.

Über diesen drei Zugängen liegt eine Normalisierung, die dasselbe Produkt bei allen sechs Händlern wiedererkennt, auch wenn jeder es anders benennt und anders verpackt. Erst dadurch vergleicht der Preisvergleich Gleiches mit Gleichem, und erst dadurch kann eine Bestellung an den günstigsten Anbieter der Woche gehen. Diese Vereinheitlichung war der Großteil der Arbeit, und sie macht aus dem Bestellen eine Entscheidung statt einer Pflichtübung.

Sechs Großhändler, erreicht über drei verschiedene Wege: rückentwickelte Bestellendpunkte, ein halbstrukturierter JSON-Feed und ein mit Playwright ausgelesener Katalog. Alle drei speisen eine Normalisierungsschicht, die dasselbe Produkt über Anbieter hinweg zusammenführt, bevor es in die gemeinsame Datenbank geht.
Sechs Großhändler, erreicht über drei verschiedene Wege: rückentwickelte Bestellendpunkte, ein halbstrukturierter JSON-Feed und ein mit Playwright ausgelesener Katalog. Alle drei speisen eine Normalisierungsschicht, die dasselbe Produkt über Anbieter hinweg zusammenführt, bevor es in die gemeinsame Datenbank geht.
LieferantenquelleZugangWas sie brechen lässt
Webshop ohne APIrückentwickelte Bestellendpunkteein Relaunch der Website
Teilweise strukturierthalbstrukturierter JSON-Feedeine Änderung am Schema
Nur Katalogmit Playwright ausgeleseneine Änderung am Markup

Eine Änderung, viele Ziele. Hier zahlt sich die gemeinsame Datenbank aus, weil eine Änderung an der Karte von einer Stelle aus in alle Richtungen wirkt: Die Website zeigt sie sofort, die hinterlegten Rezepte rechnen Portionen und Mengen neu, und diese Mengen gehen in die Lieferantenschicht, die die Bestellung auslöst.

Der saisonale Kartenwechsel, dreimal im Jahr, kostete früher Tage an Handarbeit in mehreren Systemen und ist heute eine einzige Aktion in der App. Die Übersetzung der Karte läuft denselben Weg: Ein Sprachmodell in der App übersetzt in die übrigen Sprachen, für Cent statt für rund 200 € pro Karte beim Dienstleister.

Tischstatus per NFC. Das Personal setzt den Status eines Tisches, indem es ein NFC-Tag am Tisch antippt: Getränke serviert, Essen serviert, Tisch abgeräumt. Jeder Tap ist ein Ereignis mit Zeitstempel und geht in dieselbe Realtime-Datenbank, sodass der Status sofort in allen Ansichten steht.

Die Datenbank ist der Integrations-Bus: der führende Datensatz und das Ereignis sind dieselbe Zeile.

Entscheidungen und Abwägungen

Cache-Dauer pro Datenart, nicht eine globale Konstante. Ein einziger Wert für alles erzeugte beides, veraltete Anzeigen und überflüssige Abfragen; sobald sich die Frische nach der Änderungsrate richtet, fällt beides weg. Bezahlt wird das mit einer Regel, die dann überall gelten muss: Jeder Schreibvorgang macht seinen Cache-Eintrag ungültig.

Eine Website, die nie hinterherhinkt. Sie liest aus derselben Datenbank, die die Küche pflegt, also ist eine Kartenänderung sofort öffentlich, und es gibt kein zweites System zu pflegen. Erkauft ist das damit, dass die öffentliche Seite die Struktur der Betriebsdatenbank erbt statt eines eigenen, sauberen Inhaltsmodells.

Die Lieferanten rückentwickeln und die Brüchigkeit hinnehmen. Gegen undokumentierte Endpunkte und einen Scraper zu bauen war der einzige Weg an Preise, die diese Händler nicht veröffentlichen, und solche Integrationen brechen, sobald ein Lieferant seine Seite umbaut. Die Abwägung war bewusst: Eine brüchige Anbindung, die den Preisvergleich liefert, ist mehr wert als eine stabile, die es nicht gibt. Jede Quelle ist gekapselt und wird einzeln validiert, damit ein Ausfall nicht den ganzen Katalog mitreißt.

Antippen statt durch die App navigieren. Im vollen Service sucht niemand einen Bildschirm, während ein Tap auf das Tag am Tisch nichts kostet und deshalb tatsächlich benutzt wird. Die Grenze eines solchen Systems ist die Akzeptanz und nicht das Datenmodell. Dafür sind NFC-Tags physische Infrastruktur, die angebracht und ersetzt werden muss, und die Daten sind nur so gut, wie das Personal ans Antippen denkt.

Was schiefging

Der hartnäckigste Fehler trat nur im Browser auf: Nach dem Aufwecken eines Geräts drehte sich der Ladekreis endlos. Die Erneuerung des Auth-Tokens serialisiert der Browser über ein Lock, und beim Aufwachen wartete eine Datenabfrage auf genau dieses Lock, das nie zurückkam.

Der erste Versuch war ein Timeout nach zehn Sekunden, womit die Oberfläche zwar nicht mehr hing, die nächste Abfrage aber genauso scheiterte, weil das Token weiterhin tot war. Die eigentliche Lösung erneuert die Session, bevor nach dem Aufwachen die Welle von Abfragen losläuft.

Ablauf des Fehlers: Nach dem Aufwachen wartet die Datenabfrage auf das Lock, mit dem der Browser die Token-Erneuerung serialisiert. Ein Timeout nach zehn Sekunden beendet zwar den Ladekreis, aber die nächste Abfrage scheitert erneut, weil das Token noch tot ist. Die Lösung erneuert die Session vor der Welle von Abfragen.
Ablauf des Fehlers: Nach dem Aufwachen wartet die Datenabfrage auf das Lock, mit dem der Browser die Token-Erneuerung serialisiert. Ein Timeout nach zehn Sekunden beendet zwar den Ladekreis, aber die nächste Abfrage scheitert erneut, weil das Token noch tot ist. Die Lösung erneuert die Session vor der Welle von Abfragen.

Ein Timeout erkauft eine erträgliche Oberfläche. Zur Ursache führt erst die Frage, was beim nächsten Versuch passiert.

Ein ähnliches Problem im eigenen Betrieb?

Was ich für Unternehmen mache

Sie besetzen eine Rolle in angewandter KI oder Solutions-Architektur?

Der vollständige Werdegang