Domain-driven Design macht Datenmodelle verständlich Datenmanagement als Erfolgsfaktor für Microservices
Durch die Unterstützung des DevOps-Ansatzes können Microservices dazu beitragen, sowohl die Effizienz des IT-Betriebs als auch die Art, wie Entwicklerteams arbeiten, zu verbessern. Eine der größten Herausforderungen kann das Datenmanagement sein.
Anbieter zum Thema

Im passenden Kontext sind Microservices sehr hilfreich. Aber sie können auch zu einer hohen Komplexität in Fachabteilungen und der IT führen. Vor einer Architekturentscheidung für Microservices sollten Unternehmen daher zunächst einmal die Geschäftsprozess-spezifischen Probleme, die sie damit lösen wollen, in all ihren Facetten verstehen.
Muster und Praktiken von Domain-driven Design (DDD) unterstützen sie dabei, Geschäftsprobleme zu entwirren und abzugrenzen. Ist dies erfolgt, kann die IT-Abteilung Entscheidungen darüber treffen, wie ihre Dienste zu gestalten sind und wie die Domänenmodelle Daten gemeinsam nutzen sollen.
Wollen Unternehmen mit Microservices und DDD arbeiten, sollten zunächst folgende Aspekte geklärt werden:
- Was ist die Core-Domäne? Was sind die unterstützenden Domänen?
- Wo sind die transaktionalen Grenzen der Business Use Cases?
- Wie sollten Microservices über Grenzen hinweg kommunizieren?
- Ist es sinnvoll, Daten über eine Log-Struktur zu integrieren?
Eine Domäne reflektiert die Realität
Bevor ein Entwickler einen Mikroservice erstellt und analysiert, welche Daten er produziert und konsumiert, ist ein klares Verständnis darüber notwendig, was diese Daten repräsentieren. Bevor zum Beispiel Informationen über „Buchungen“ in der Datenbank eines Reservierungssystems gespeichert und dessen Migration auf Microservices vollzogen wird, muss klar definiert sein, was eine „Buchung“ ist. Relevant für die Domäne „Unternehmen“ ist ferner ein Verständnis dafür, was beispielsweise eine „Rechnung“, ein „Mitarbeiter“, eine „Forderung“ oder eine „Lieferung“ ist.
Wichtig ist, dass eine Domäne die Realität reflektiert und sie so umsetzt, dass sie bei der Lösung eines Problems hilft. Dazu gehört auch, dass es eine Grenze gibt, in der die Domäne gültig und die Terminologie eindeutig ist. Das Datenmodell, das heißt, wie Konzepte in einem physischen Datenspeicher dargestellt werden sollen, wird vom Domänenmodell gesteuert, nicht umgekehrt.
Sind die Domänengrenzen bekannt, lassen sich Aussagen darüber treffen, was in einem Domänenmodell konsistent ist und was „außerhalb der Grenzen liegt“. Diese Grenzen bedeuten auch ein gewisses Maß an Autonomie. Der abgegrenzte Kontext „A“ kann ein anderes Verständnis davon haben, was ein „Buch“ ist als der abgegrenzte Kontext „B“. A ist beispielsweise ein Service, der nach Titeln sucht, bei denen ein einzelner Titel ein „Buch“ ist. Der abgegrenzte Kontext „B“ kann ein Kassenservice sein, der eine Transaktion verarbeitet, die darauf basiert, wie viele und welche Bücher ein Kunde kauft.
Transaktionale Grenzen festlegen
Entwickler benötigen ein Domänen-gesteuertes Design, um die Datenmodelle zu verstehen, mit denen sie Systeme implementieren, und sie müssen Grenzen um diese Modelle in einem Kontext ziehen. Damit wird auch klar, dass „Kunde“, „Konto“ und „Buchung“ verschiedene Dinge in unterschiedlichen Kontexten bedeuten können. Zunächst aber müssen die transaktionalen Grenzen festgelegt werden.
Eine transaktionale Grenze im Sinne einer „Arbeitseinheit“ ist die kleinste atomare Formation, die in Bezug auf die Geschäftsinvarianten benötigt wird. Diese „Arbeitseinheit“ sollte so klein wie möglich sein, beispielsweise eine einzelne Transaktion mit einem einzigen Objekt, damit sie skaliert werden kann.
Erstellen Entwickler ein Domänenmodell in der DDD-Terminologie. identifizieren sie Entitäten, Wertobjekte und Aggregate. Aggregate in diesem Umfeld sind Objekte, die andere Entitäten beziehungsweise Wertobjekte kapseln und für die Durchsetzung von Invarianten zuständig sind. Innerhalb eines abgegrenzten Kontexts können mehrere Aggregate existieren.
Microservices kommunizieren über Grenzen hinweg
Um die Geschäftsinvarianten intakt zu halten, können Entwickler diese mit DDD als Einzelaktionen mit einem Aggregat modellieren. Was hier zählt, ist ein möglichst hoher Grad an Autonomie. Dennoch wird in der Praxis irgendeine Form der Konsistenz zwischen den Aggregaten und zwischen abgrenzten Kontexten benötigt.
Um Konsistenz zu erzielen, sollte zwischen transaktionalen Grenzen und abgegrenzten Kontexten über Domänenereignisse (Events) kommuniziert werden. Aggregate könnten Befehle und Domänenereignisse direkt verwenden. Jede Operation wird als Befehl und jede Antwort als Reaktion auf Ereignisse implementiert. Damit lässt sich eindeutig zwischen den Ereignissen unterscheiden, die intern in abgegrenzten Kontexten verwendet werden, und denen, die zwischen Kontexten verwendet werden.
Eine weitere Option ist es, Ereignisse in einer Messaging-Warteschlange zu veröffentlichen, diese mit einem Listener aus der Warteschlange abzurufen und idempotent in die Datenbank einzufügen, ohne XA/2PC (eXtended Architecture/two-Phase Commit)-Transaktionen verwenden zu müssen. Ferner lässt sich das Ereignis in einen speziellen Ereignisspeicher einfügen, der sich sowohl wie eine Datenbank als auch wie ein Messaging-Publish-Subscribe-System verhält – für viele wird dies wahrscheinlich der bevorzugte Weg sein.
Es ist zudem möglich, eine ACID-Datenbank (ACID = Atomacy, Consistency, Isolation, Durability) einzusetzen und die Änderungen in die Datenbank mit einem persistenten, replizierten Log wie Apache Kafka und Debezium zu streamen. Egal für welche Methode Entwickler sich entscheiden, der entscheidende Punkt ist: Die Kommunikation über Grenzen hinweg erfolgt abhängig von einem unveränderlichen Ereignis.
Die Datenbankarchitektur von innen nach außen kehren
Im nächsten Schritt wäre es denkbar, die im letzten Absatz skizzierten Konzepte konsequent weiterzuführen: Ereignisse und Streams kommen für alles zum Einsatz und die Ereignisse sind persistent. Die Datenbanken, Caches und Indizes sind dann nur materialisierte Ansichten eines persistenten Logs beziehungsweise Event Streams, die in der Vergangenheit passiert sind, und der aktuelle Zustand ist eine „Falte“ über all diesen Ereignissen.
Damit lassen sich beispielsweise neue Anwendungen einführen, um vergangene Ereignisse erneut damit zu nutzen und ihr Verhalten im Hinblick auf „Was-wäre-passiert, wenn-Szenarien“ zu untersuchen. Weitere Informationen zu diesem Ansatz gibt es in einem Blog von Martin Kleppmann mit dem Titel „Turning the database inside-out with Apache Samza”.
* Christian Posta ist Chief Architect, Cloud Application Development, bei Red Hat.
(ID:45376593)