Implementierung eines Supermarkt-Self-Checkout mit DDD – Ein Forschungsprojekt auf Basis von XOOM

Brick and Mortar 2.0 — no shoplifter, but tech hipster

Supermärkte mit Self-Checkout — also Bezahlen ohne Kasse und Kassierer — gibt es schon eine Weile. Seit ein paar Jahren kann man so auch in Deutschland einkaufen. Technisch funktionieren diese Self-Checkouts so, dass man vor dem Verlassen des Supermarktes alle Waren an einer Kassierstation scannt und anschließend bezahlt. Der Kunde übernimmt also die Aufgabe des Kassierers. Ist allerdings keine Self-Checkout-Kasse frei, so muss man warten und oftmals bilden sich dabei Warteschlangen. Warten, sowie Ein- und Ausräumen kostet wertvolle Zeit — … und Nerven. Viel komfortabler wäre es ohne diese Pain-Points.

Customers self checkout

Einfach aus dem Laden rausgehen, ohne vorher zu scannen, funktioniert nicht. Das ist Diebstahl und ruft den Ladendetektiv auf den Plan. Irgendwie muss also vorher eine automatische Erfassung und Abrechnung der Waren stattfinden. Genau daran wird aktuell geforscht, indem die Aufgaben des Kassierers verlagert werden. Man müsste sich nicht mehr die Mühe machen, die Waren vor dem Verlassen des Supermarktes einzeln zu erfassen. Warteschlangen könnten damit der Vergangenheit angehören — Einfach den Einkaufskorb füllen und rausgehen, anschließend wird automatisch abgerechnet. Die Abrechnung ist bequemer: Wie man es beim Onlineshopping kennt, erfolgt die Abrechnung beim Verlassen einfach über einen beliebigen Zahlungsdienstleister.

Uncovering and Distilling Knowledge with EventStorming and Domain-Driven Design — You’ve got questions, we’ve got answers.

So hatte ich mir die Zukunft vorgestellt, dessen Umsetzung ich mir als Herausforderung für meine Masterarbeit an der Westsächsischen Hochschule Zwickau gewählt hatte. Im Frühjahr 2020 habe ich folglich mit der Planung und Entwicklung eines Prototypen begonnen. Von Anfang an war klar, dass die Umsetzung solcher Einkaufs-Szenarien die Softwareentwickler vor enorme Herausforderungen stellt. Das Thema ist fachlich komplex und birgt viele Unbekannte. Mehrere Fachbereiche aus dem Supermarktumfeld sind an der Entwicklung beteiligt und haben Einfluss auf die Umsetzung der Software. Um die Gefahr von Missverständnissen und Fehlentwicklungen zu verringern, war es essentiell, von den Fachleuten zu lernen — durch Gespräche oder Aktivitäten, die den Wissenstransfer fördern. Darum wurde zu Beginn des Projektes ein Workshop durchgeführt, bei dem Fachleute und Softwareentwickler miteinander die Geschäftsabläufe diskutieren und modellieren — in Zeiten von Corona natürlich kontaktlos. EventStorming war das Mittel der Wahl um die Abläufe zu visualisieren. Hierbei geht es darum, Geschäftsabläufe zunächst mit Hilfe von Events zu beschreiben. Events repräsentieren Vorgänge, die im fachlichen Ablauf geschehen sind, z.B., dass ein Item soeben in einen Warenkorb gelegt wurde (“Item added to basket”).

Das Großartige an EventStorming ist, dass für die Durchführung von den Teilnehmern keinerlei technisches Wissen oder generelles Vorwissen zu dieser Methodik benötigt wird. Im ersten Schritt, der Chaotic Exploration verschaffen sich die am Projekt Beteiligten einen ersten Überblick über die Fachlichkeit, also die Geschäftsprozesse. Mit Post-It-Notes werden die Abläufe an einer großen (virtuellen) Wand sichtbar gemacht.

Weil die Notation frei von Verbindungen ist und nur auf Stickies beruht, bleiben die dargestellten Abläufe flexibel änderbar — Stickies lassen sich umsortieren, um einen anderen Ablauf zu visualisieren, und man kann sie zur Seite packen, falls sich in Diskussionen herausstellt, dass der Ablauf doch anders ist. Das lädt zum Experimentieren anhand der Abläufe ein und fördert den Austausch zwischen Fachleuten und Softwareentwicklern.

Experimentieren wurde im Projekt und in meiner Masterarbeit großgeschrieben. Viele fachliche Abläufe waren am Anfang des Projektes nicht bekannt; wie so oft war nicht klar, in welche Richtung sich die Anforderungen entwickeln würden. Es war klar, dass experimentiert werden muss, um eine funktionierende Lösung in akzeptabler Zeit entwickeln zu können. Hätte ich das Projekt lediglich als Prototypen betrachtet und Code schnell nach dem Methode “Hirn ins Terminal” geschrieben, hätte das Projekt spätestens nach ein zwei, drei Monaten den Zustand des “Big Ball of Mud” erreicht. Die Umsetzung von Änderungen hätte zu lange gebraucht und die fürs Experimentieren so notwendige Flexibilität und einfache Anpassbarkeit der Software wäre abhanden gekommen.

Das strategische Design in Domain-driven Design (DDD) beinhaltet Techniken, durch welche die Kommunikation zwischen Fachleuten und Entwicklern verbessert werden kann und das Wissen der Fachleute direkt in der Struktur der Software wiederzufinden ist. Das Strukturieren wird als Context Mapping bezeichnet. Dabei geht es darum, eine Übersicht über alle beteiligten Domänen zu erstellen. In der Context Map stellen große Ovale einen Bounded Context dar und Verbindungen visualisieren Beziehung zwischen diesen. Diese Beziehungen beschreiben sowohl technische Mechanismen als auch soziologische Strukturen zwischen den an der Entwicklung der Software beteiligten Teams. Das soziologische Verhältnis beschreibt dabei beispielsweise Hierarchien, welche wiederum die technische Integration beeinflussen. So wird durch Context Maps der Verhältnis zwischen Bounded Contexts und den Teams, die diese entwickeln, explizit beschrieben und Abhängigkeiten werden sichtbar. Das System des kassenlosen Supermarkts hat sich übrigens wie in der nachfolgend gezeigten Context Map aufgegliedert.

Die Fachlichkeit sollte den Quellcode dominieren, damit sich Fachkonzepte leicht umsetzen und anpassen lassen. Technische Belange durften dem Entwicklungsprozess nicht im Weg stehen. Für diesen Zweck bietet DDD sogenannte taktische Muster. Dabei handelt es sich um Entwurfsmuster, welche jeweils einem bestimmten Zweck zu erfüllen, damit fachlichen Vorgänge im Quellcode abgebildet werden können. Entities stellen eine individuelle einzigartige Sache dar und Value Objects Ausprägungen von Werten. Indem Entities und Value Objects miteinander kombiniert, können Konzepte der Realität modelliert werden. In DDD werden Entities und Value Objects zu Aggregaten zusammengefasst. Aggregate stellen sicher, dass fachliche Regeln (sogenannte Invarianten) immer eingehalten werden; sie sorgen für atomares Änderungsverhalten der zugehörigen Entitäten und Value Objects.

In der obigen Abbildung wird beispielsweise das fachliche Konzept Warenkorb abgebildet. Ein Aggregate hat immer einen Eingangspunkt, die sogenannte Root Entity. In diesem Fall übernimmt die Basket-Entität die Rolle der Aggregatwurzel. Die Root Entity gilt als Eingangspunkt in einen Teil des Modells; im Beispiel kann daher weder Items nicht direkt modifiziert werden. Durch das Aggregate soll sichergestellt werden, dass das gesamte Modell einen konsistenten Zustand bewahrt. Wird beispielsweise dem Basket ein neues Item hinzugefügt, würde sich der TotalPrice des Basket in Abhängigkeit der Quantity und des UnitPrices verändern. Dies erleichtert auch das Persistieren und vermeidet unnötig viele Transaktionen, da der neue Zustand des Aggregats in einer einzigen Transaktion gesichert werden kann.

Ein Datenmodell, welches nur aus Datenfeldern besteht und lediglich Getter und Setter implementiert, ist kein Fachmodell. In DDD wird ein solches Modell als “blutleeres Modell” bezeichnet. Um dem Modell Leben einzuhauchen, sollte das Modell Funktionen bereitstellen, die einen Bezug zu den fachlichen Vorgängen der Domäne darstellen. Beim kassenlosen Supermarkt ist eine solche Funktion beispielsweise das Legen eines Items in den Warenkorb. Dieser Vorgang erscheint auf den ersten Blick völlig simpel und banal. Hat man direkten Zugriff auf die Liste der Items des Warenkorbs, so muss man lediglich die add-Funktion aufrufen und den neuen Artikel hinzufügen. In der Domäne des kassenlosen Supermarkts ist dieser Vorgang aber etwas komplexer. Liegt das Item bereits im virtuellen Warenkorb? Wenn ja, dann soll die Anzahl erhöht werden, ansonsten muss ein neuer Item-Eintrag im Warenkorb angelegt werden. Wie verändert sich der Gesamtpreis? Basierend auf dem neu hinzugefügten Item, muss auch der neu entstehende Gesamtwert des Warenkorbs angepasst werden. Zukünftig wäre auch denkbar, dass weitere Faktoren den Gesamtwert des Warenkorbs beeinflussen. Gibt es eine Aktion, bei welcher man drei Items zum Preis von zwei kaufen kann? All dies muss berücksichtigt werden.

Ein Modell alleine genügt nicht für den kassenlosen Supermarkt. Erst wenn die Modelle miteinander kommunizieren und sich gegenseitig mitteilen, was gerade passiert ist oder ein anderes Modell etwas gerade ausführen soll, kann die Gesamtheit der fachlichen Prozesse in der Software abgebildet werden. Für diese Zwecke werden Commands und Domain Events eingesetzt. Commands und Events sind zunächst nichts weiter als Datenstrukturen mit lesbaren Daten. Sie besitzen kein Verhalten.

Ein Command drückt eine Absicht aus, dass ein bestimmtes Aggregat etwas tun soll. Commands werden im Imperativ formuliert, wie beispielsweise “Entferne das Item aus dem Regal”.

Ein Domain-Event hingegen drückt aus, dass in einer Domäne gerade etwas passiert ist. Beispielsweise wenn dem Basket-Aggregat gerade ein neues Item hinzugefügt wurde und sich dadurch der Zustand des Aggregates verändert hat, so wird im Anschluss ein Domain-Event “Es wurde gerade ein Item in einen Warenkorb gelegt” emittiert, welches andere Domänen über diesen Fakt bzw. fachlichen Vorgang informiert.

Aggregate kommunizieren untereinander mittels Domain Events. Andere Aggregate, die an einem fachlichen Ereignis interessiert sind, können die Domain Events empfangen und mit den darin enthaltenen Daten weitere Aktionen durchführen. Betritt beispielsweise ein Kunde den kassenlosen Supermarkt, löst der Bounded Context Check-In ein Event aus, welches der Bounded Context Basket empfängt, dieser übersetzt das Event in den internen Befehl  CreateBasket, woraufhin ein neues Aggregat für einen virtuellen Warenkorb angelegt wird. Events werden über einen Message-Bus zwischen Bounded Contexts ausgetauscht.

XOOM for the Win

XOOM ist eine Open-Source-Plattform für die Entwicklung reaktiver domänengetriebener Software. Ich habe XOOM in meiner Masterarbeit eingesetzt, weil mein Betreuer an der Westsächsischen Hochschule, Professor Grimm, es mir empfohlen hatte, da es die DDD-Prinzipien sehr gut umsetzt. XOOM erlaubt es, auch fachlich komplexe Modelle direkt in Software abzubilden — ohne, dass die Fachlichkeit unter technischen Belangen (wie zum Beispiel Microservices, Threading, Persistierung, Messaging usw.) begraben und die Software unwartbar wird.

Ausgehend von dem Event-Storming-Modell, das zu Beginn meiner Masterarbeit in Zusammenarbeit mit meinen Kollegen entstanden ist, habe ich begonnen, einen ersten Prototypen der Software mit XOOM zu implementieren. Nun ist natürlich die Frage, wie es möglich ist, mithilfe eines Event-Storming-Modells, ein Softwaresystem zu entwickeln. Das Praktische hierbei ist, dass sich die Konzepte aus EventStorming in XOOM wiederfinden. Die im EventStorming genannten Commands können direkt in Software implementiert werden und sorgen dafür, dass Methoden an Objekten aufgeführt werden. Diese Commands werden auf Aggregaten angewandt, welche in XOOM durch Actors realisiert sind. Man kann sich Actors wie kleine Rechenmaschinen vorstellen, welche Eingaben entgegennehmen und diese asynchron verarbeiten. Wenn ein Actor einen Command erfolgreich durchführt und dabei seinen Zustand ändert, so emittiert der Actor anschließend ein Event. Die Zustandsveränderung könnte bedeuten, dass ein Item in einen Warenkorb gelegt wurde und weil diese Zustandsveränderung Systemweit bekannt gegeben wird, können andere Softwarekomponenten darauf entsprechend reagieren.

Mit XOOM kann Event Sourcing umgesetzt werden. Hierbei dienen Event-Sourced Entities ähnlich wie bei andere Frameworks zur Persistierung des Zustands. Eine Sequenz von Domain Events repräsentiert den Zustand eines Aggregats (d.h., dessen Entitys und Value Objects). Diese Events werden persistiert. Soll ein Aggregat später erneut verwendet werden, obwohl es sich zur Laufzeit nicht mehr im Arbeitsspeicher befindet, so kann sein aktueller Zustand durch erneutes Anwenden aller Events (“Abspielen” der Events) wieder hergestellt werden.

All diese technischen Details erscheinen ziemlich komplex und es würde viel Zeit kosten, diese von Hand zu implementieren. Jedoch hatten wir uns zu Beginn hatten das Ziel gesetzt, dass technische Belange der Fachlichkeit nicht im Wege stehen sollen und Konzepte wie Events und Commands leicht in der Software abbilden lassen. Für diesen Zweck kam der XOOM Designer wie geschaffen. Der XOOM Designer bietet eine grafische Oberfläche, über welche die uns bekannten Aggregate, Events und Commands abgebildet werden, die aus den vorhergehenden Aktivitäten entstanden sind. So kann man Stück für Stück die Fachlichkeit modellieren und anschließend eine lauffähige Software generieren. Es benötigt keinerlei zusätzlichen Aufwand, damit Messaging gelingt, mit Ausnahme einer lauffähigen RabbitMQ Instanz, worüber die Domain-Events zwischen den Microservices ausgetauscht werden. Neben den Events und Commands, kann auch ein Persistierungs-Mechanismus gewählt werden, wie beispielsweise das zuvor genannte Event Sourcing, wodurch die mühselige Implementierung entfällt. Nachfolgend ist ein Beispiel, wie das Basket-Aggregat im Xoom Designer modelliert werden kann.

In Organisationen, welche verteilte Anwendungen entwickeln ergibt sich häufig das Problem, dass der Überblick verloren geht, welche Informationen ausgetauscht werden. Weitere Herausforderungen entstehen, wenn sich die Struktur der ausgetauschten Informationen aufgrund neuer Versionen verändern. Tritt dieser Wandel ein, hat dies Auswirkungen auf alle von dieser Information abhängigen Dienste. Im schlimmsten Fall ist nicht bekannt, welche Dienste vom neuen Nachrichtenformat abhängig sind und einer dieser Dienste die neue Nachrichtenstruktur nicht. Gibt es also eine Möglichkeit den Überblick über die ausgetauschten Informationen zu bewahren oder die Abhängigkeiten der Services von Nachrichten nachvollziehbar zu machen?

Genau diese Probleme adressiert XOOM Schemata. Bei XOOM Schemata handelt es sich um eine Schema Registry. Der Zweck dieser ist es, den Informationsaustausch innerhalb einer Organisation zu verwalten und für Teile der Organisation zugänglich zu machen. Dieses Vorgehen ist verwandt mit dem Domain-driven Design Konzept der Published Language. In der Schema-Registry werden alle Nachrichten veröffentlicht. Dadurch wird transparent gemacht, welche Nachrichten in der gesamten Organisation ausgetauscht werden. Mittels einer Domänenspezifischen Sprache wird das Format einer Nachricht spezifiziert und basierend darauf kann Quellcode generiert werden. Möchte ein anderer Bounded Context diese Nachricht nutzen, so kann dieser sich dafür Quellcode generieren lassen, was besonders dabei hilft Typensicherheit zu wahren.

Ändert sich ein bestehendes Nachrichtenformat oder wird ein neues verfügbar, so kann dieses in die Schema-Registry publiziert werden. Weil die Schema-Registry verwaltet, welche Dienste ein bestimmtes Nachrichtenformat nutzen, ergeben sich dadurch Möglichkeiten, wie mit diesem Wandel umgegangen werden kann. Dem Producer der Nachricht ist ersichtlich, welche Consumer das alte Nachrichtenformat nutzen. Daher kann dieser sich dazu entscheiden, zwei unterschiedliche Versionen zu unterstützen, damit abhängige Dienste weiterhin kompatibel sind. Konsumenten hingegen können sich dafür entscheiden, auf die neue Version des Nachrichtenformats umzusteigen. Beispielsweise, falls das alte Nachrichtenformat zu einem bestimmten Zeitpunkt nicht länger unterstützt werden soll. Auf diese Art und Weisen hilft XOOM Schemata dabei, beim Nachrichtenaustausch den Überblick zu bewahren und langfristig eine nachhaltige Weiterentwicklung zu unterstützen.

Fazit

EventStorming, Domain-Driven Design und XOOM haben mir geholfen, in nur vier Monaten einen ersten Prototypen zu implementieren und erfolgreich zu testen. EventStorming als leichtgewichtige Modellierungstechnik ist nützlich, weil sie vielseitig und auch für Nicht-Techniker, sowie das Management leicht verständlich ist. XOOM ermöglicht es, Konzepte aus EventStorming in die Implementierung direkt zu übernehmen und erleichtert dadurch die Entwicklung. Weil das Design der Software die Sprache und Konzepte der Fachleute widerspiegelt, führt dies zum besseren Verständnis des Codes, mehr Wartbarkeit, höherer Anpassbarkeit und letztlich guter Codequalität.

Und immer wichtig: Mit Fachleuten reden und die Fachlichkeit bei der Entwicklung nicht aus den Augen verlieren!

Dr. Frank Grimm

Dr. Frank Grimm

Dr. Frank Grimm is a professor of informatics at Zwickau University of Applied Sciences, Germany teaching and researching various software development topics — amongst them reactive systems and Domain-Driven Design using XOOM.

More to explore

Reactive DDD: Modeling Uncertainty

Domain-Driven Design supports reactive architecture and programming. Still, reactive introduces uncertainty. Back in 2003, the way that Domain-Driven Design was used and

Scroll to Top