Blog

Technology behind the Ordering Stack - Ordering Stack

Geschrieben von 3esoftwarehouse | 18.10.2021 23:00:00

Hauptkonzept

Ordering Stack ist eine E-Commerce- und POS-Plattform für Restaurantketten

Ordering Stack besteht aus Backend-Services und Frontend-Anwendungen. Es gibt Services für Authentifizierung, Produkte und Menüs, Standorte, Zahlungen, Bestellungen und weitere – sie stellen die geschäftliche Funktionalität für jeweils spezifische Geschäftsbereiche des Systems bereit. Jeder dieser Services stellt eine eigene REST-API zur Verfügung. Um auf diese Services zuzugreifen, müssen Clients zunächst Zugriffstoken vom Authentifizierungsservice abrufen. Mit diesem Token können die Clients API-Endpunkte aufrufen, wobei die Authentifizierungsdaten im Header der Anfrage übermittelt werden.

Neben den API-Aufrufen empfangen die Clients auch asynchrone Nachrichten vom Backend über Websockets. Diese Nachrichten enthalten den Status von Bestellungen und andere Benachrichtigungen.

Die grundlegende Idee bei der Arbeit mit dem Backend besteht darin, API-Methoden aufzurufen und Statusaktualisierungen über Websocket zu empfangen. Client-Anwendungen müssen auf asynchrones Arbeiten vorbereitet sein.

Architekturübersicht

  • Nicht blockierend & asynchron 

Benutzerbefehle und Statusaktualisierungen werden über zwei unabhängige Kanäle übertragen: HTTP-Anfragen und WebSockets

  • Es funktioniert wie bei Mehrspieler-Online-Spielen (MMOG).

- Der gesamte Status wird serverseitig gespeichert.

- Benutzer senden Befehle an den Server, ohne auf die tatsächliche Ausführung der Aktion zu warten – sie erhalten lediglich eine Bestätigung, dass der Befehl erfolgreich in die Warteschlange aufgenommen wurde.

- Der aktualisierte Status wird asynchron übermittelt.

Eine Beschreibung zur Nutzung der APIs und WebSockets finden Sie hier: https://docs.orderingstack.com/api-examples/ 

Derzeit sind alle unsere Client-Anwendungen als React.js-Anwendungen umgesetzt.

So funktioniert das Backend

Bei der Diskussion über die Architektur von Ordering Stack wollten wir den Prinzipien des Domain Driven Design (DDD) folgen und einzelne Geschäftsbereiche in separate (Micro-)Services kapseln. Jeder Domain-Service sollte dabei möglichst lose mit anderen Services gekoppelt sein und deren APIs nur selten aufrufen – insbesondere, wenn es um Datenabrufe außerhalb des eigenen Bounded Context geht.

Ein weiterer Aspekt war die Implementierung eines asynchronen Bestellprozesses: eingehende Befehle über die Ordering-API und Rückmeldungen über WebSockets. Wir mussten mehrere Services koordinieren, da die einzelnen Phasen der Bestellabwicklung aufeinander folgten. Dafür setzten wir eine Event-Streaming-Plattform ein.

Das lässt sich wie folgt visualisieren:

 


Diagramm mit Apache Kafka und mehreren Services als Publisher und Subscriber

Bitte beachten Sie, dass die Message-Broker- bzw. Event-Streaming-Plattform das zentrale Rückgrat des gesamten Systems bildet. Zum einen ermöglicht sie den Datenaustausch zwischen den Services, wodurch eine zu enge Verzahnung und direkte Interaktion vermieden wird. Jeder Microservice kann seine eigenen Daten – auch die von anderen Services – verwalten und bei Bedarf aktualisieren, sobald entsprechende Events empfangen werden.

Zum anderen erlaubt sie die Choreografie von Services: Sie reagieren auf eingehende Events und publizieren neue Events, um andere Services zu aktivieren. Dieser Ansatz macht die gesamte Lösung agiler, leistungsfähiger (nicht blockierend), fehlertoleranter und robuster. Ein konkreter Vorteil ist zum Beispiel, dass wir problemlos die Unterstützung von Webhooks in den Bestellprozess integrieren konnten.

Aus technischer Sicht kamen folgende Technologien zum Einsatz:

  • Die Microservices sind in Java mit Spring Boot oder Node.js implementiert.

  • Wenn ein Service eine Datenhaltung benötigt, wird MongoDB verwendet. (Services dürfen keine Datenbanken gemeinsam nutzen.)

  • Die Microservices nutzen Service Discovery über Eureka, um andere Services zu finden.

  • Jeder Microservice läuft in einem Docker-Container. Für die Orchestrierung wird Docker Compose verwendet.

  • Als Event-Streaming-Plattform kommt Apache Kafka zum Einsatz.

Mehrmandantenfähigkeit

Ordering Stack ist eine Cloud-SaaS-Plattform und kann von mehreren Kunden (Mandanten) gleichzeitig genutzt werden. Jeder Microservice im System unterstützt Mandantenfähigkeit. Mandantenkennungen sind stets im Zugriffstoken codiert. Verfügt ein Service über eine eigene Datenhaltung, so werden die Daten der einzelnen Mandanten in getrennten Collections gespeichert. Darüber hinaus hat jeder Mandant eigene Topics für die Bestellverarbeitung auf der Event-Streaming-Plattform (Kafka).

Performance

Essensbestellungen weisen charakteristische Nutzungsmuster im Wochen- und Tagesverlauf auf. Typischerweise gibt es Bestellspitzen zur Mittagszeit und am Abend. Der Montag ist in der Regel schwächer, während Freitag, Samstag und Sonntag die stärksten Tage sind. Zusätzlich können Wetterbedingungen die Bestellzahlen stark beeinflussen – starker Regen oder Schneefall führen oft zu einem spürbaren Anstieg. Nicht zuletzt können auch Marketingaktionen wie zeitlich gezielte Werbemaßnahmen oder besondere Tage wie der ‚Internationale Pizzatag‘ einen regelrechten Ansturm auf das System auslösen.

Angesichts all dieser Faktoren muss eine Bestellplattform auf hohe Last vorbereitet sein. Deshalb haben wir bei der Konzeption von Ordering Stack Performance und Skalierbarkeit zu unseren obersten Prioritäten gemacht. Wir wollten unbedingt vermeiden, dass Menüs oder komplexe Produktstrukturen während der Anfrageverarbeitung berechnet werden müssen, oder dass etwa Rabattberechnungen den Bestellprozess blockieren. Aber wie lässt sich das erreichen, wenn bestimmte Logiken – wie Webhooks – außerhalb des Kernsystems liegen und vom Mandanten frei konfigurierbar sind? Auf deren Performance haben wir schließlich keinen Einfluss! Die Antwort lautet: Asynchronität.

 Alle bestellbezogenen Aktionen – wie das Hinzufügen eines Produkts zum Warenkorb – werden als Kommandos über REST-Anfragen an das Backend gesendet. Diese Kommandos werden in den Kafka-Stream gestellt und dort von mehreren Microservices aus unterschiedlichen Domänen wie Menüprüfung, Bestellverarbeitung, Rabatte, Treueprogramme usw. verarbeitet. Sobald ein neuer Bestellstatus vorliegt, wird dieser über WebSocket an die Bestellanwendung zurückgesendet. Zudem werden alle externen Aufrufe mit präzise definierten Timeouts und Circuit-Breaker-Mustern abgesichert, um die Zuverlässigkeit zu erhöhen.

Es ist wichtig zu erwähnen, dass alle Microservices, die an den Kafka-Stream angebunden sind, zustandslos (stateless) sind und horizontal skaliert werden können – im Einklang mit der Kafka-Architektur. Kafka garantiert dabei die Reihenfolge der Ereignisverarbeitung selbst bei mehreren Instanzen, indem alle Events, die zu einer Bestellung gehören, in dieselbe Topic-Partition geschrieben werden. Jede Partition kann nur von einem einzelnen Thread verarbeitet werden – das ist so vorgesehen.

All diese Techniken machen Ordering Stack zu einem leistungsstarken und hochverfügbaren System, das selbst extreme Lastspitzen problemlos bewältigen kann.

Test- und Produktionsumgebung

Wir setzen auf bewährte Hosting- und Cloud-Anbieter wie 3S und Microsoft Azure für das Hosting unserer Lösung. Derzeit befinden sich alle Server innerhalb der Europäischen Union.

Die meisten Systemartefakte sind mit Docker containerisiert. Für CI/CD verwenden wir GitLab und weitere Dienste zur Überwachung der Funktionsweise all unserer Umgebungen.

Möchten Sie mehr erfahren? Kontaktieren Sie uns!
Wir sprechen gerne über Technologie!
Kontaktieren Sie uns

+48 725 935 000

info@orderingstack.com