Was ist Domain-Driven Design (DDD)?

the next big thing Golo Roden  –  49 Kommentare

Domain-Driven Design (DDD) gibt es zwar bereits seit 2004, doch hat sich das Vorgehen in all der Zeit nicht übermäßig weit verbreiten können. Seit einigen Jahren erlebt der Begriff jedoch einen zweiten Frühling. Daher ist es an der Zeit zu fragen, was Domain-Driven Design eigentlich ist.

Softwareentwicklung ist kein Selbstzweck. Stattdessen wird Software entwickelt, um fachliche Probleme aus der realen Welt zu lösen. Dazu bedarf es Technologie, doch diese steht dabei nicht im Mittelpunkt, sie ist lediglich Mittel zum Zweck. Den tatsächlichen Mittelpunkt bildet die Fachlichkeit, weshalb ein gutes Verständnis von ihr essenziell für eine erfolgreiche und zielgerichtete Entwicklung ist.

Die Forderung, ein fachliches Verständnis aufzubauen, gilt in besonderem Maße auch für Entwicklerinnen und Entwickler. Denn letztlich bildet Software nicht das Wissen von Fachexpertinnen und -experten ab, sondern ist lediglich die Interpretation dessen, was in der Entwicklung von der Fachlichkeit verstanden wurde – und das kann dem, was eigentlich gewünscht war, mehr oder weniger entsprechen.

Ein gutes Fachverständnis entsteht durch eine regelmäßige Kommunikation und eine gemeinsame Sprache, was vor allem in interdisziplinären Teams eine Herausforderung darstellt: Schließlich verfügt jede Disziplin über eine eigene Fachsprache, weshalb Missverständnisse vorprogrammiert sind.

Ein gutes Beispiel dafür ist das Wort "regelmäßig", das im Alltag so viel bedeutet wie "stetig wiederkehrend", im juristischen Sinne aber für "einer Regel gemäß" steht. Auch das Wort "Reichweite" hat für jemanden aus dem Online-Marketing eine ganz andere Bedeutung als für jemanden aus der Automobilbranche. Hier gilt es also, Missverständnisse zu erkennen und zu beheben.

Genau das ist das Ziel von Domain-Driven Design (DDD). Der Begriff stammt von Eric Evans, der ihn 2004 als Bestandteil des Titels seines Buchs "Domain-Driven Design: Tackling Complexity in the Heart of Software" geprägt hat. Die Kernidee ist, die Domäne – also die Fachlichkeit – bei der Softwareentwicklung in den Vordergrund zu rücken und dafür Sorge zu tragen, dass bei allen beteiligten Personen unabhängig von ihrer jeweiligen Disziplin ein gemeinsames Verständnis der fachlichen Sprache besteht.

Dazu schlägt Evans eine Reihe von Begriffen und Werkzeugen vor, die beim Modellieren der Domäne und dem Finden der gemeinsamen Sprache, die er als Ubiquitous Language bezeichnet, helfen können. Letztlich geht es bei Domain-Driven Design also um Empathie, Verständnis und das Finden der passenden Begriffe für die fachlichen Artefakte.

Was ist Domain-Driven Design (DDD)?

Commands

Der erste Ansatz, um diesem Ziel der Ubiquitous Language näher zu kommen, besteht darin zu erkennen, dass Anwenderinnen und Anwender Ziele haben und Intentionen verfolgen, wenn sie Software verwenden. Niemand ändert eine Zahl, um eine Zahl zu ändern, sondern um der damit verbundenen fachlichen Auswirkung wegen.

Die Intention beim Ändern einer Zahl könnte beispielsweise sein, den Umsatzsteuersatz anzupassen, um den neuen gesetzlichen Vorgaben zu entsprechen. Ein technisches Update entpuppt sich auf dem Weg als das fachliche Konstrukt der Steueranpassung. Derartige Semantik und Intention gilt es in der Modellierung und Implementierung von Software abzubilden.

Selbstverständlich muss die fachliche Intention letztlich in ein technisches Konstrukt übersetzt werden, denn aus technischer Sicht handelt es sich bei dem genannten Beispiel tatsächlich um ein Update. Der Fehler, der ohne DDD häufig gemacht wird, besteht aber darin, diese Übersetzung nicht möglichst spät zu machen, sondern sie bereits auf API-Ebene und damit auch in der UI durchzuführen, wodurch in weiten Teilen der Anwendung die fachliche Intention verloren geht.

Als Werkzeug stellt DDD dafür das sogenannte "Command" zur Verfügung. Ein Command ist ein Objekt, das eine fachliche Intention kapselt. Es trägt daher einen beschreibenden Namen, beispielsweise "Storniere die Bestellung", verfügt zusätzlich aber auch noch über Daten und Metadaten. Während die Daten die fachlichen Details liefern (beispielsweise um welche Bestellung es geht), enthalten die Metadaten technische Parameter wie einen Zeitstempel oder eine ID.

Als Faustregel gilt, dass der Name eines Commands aus einem Verb im Imperativ und einem Substantiv besteht. Das muss nicht immer der Fall sein, aber es dient zumindest als gute Vorlage. Darüber hinaus ist wichtig zu verstehen, dass ein Command von der Anwendung, an die das Command geschickt wird, nicht zwingend verarbeitet werden muss – es kann durchaus auch abgelehnt werden, wofür es verschiedene fachliche oder technische Gründe geben kann.

Commands in Domain-Driven Design (DDD)

Domain-Events

Begreift man ein Command als Aktion, liegt es nahe, dass als Kehrseite der Medaille eine Reaktion erwartet wird. Diese stellen in DDD die sogenannten "Domain-Events" dar, die einen Fakt beschreiben, der passiert ist und sich auch nicht ungeschehen machen lässt. Die Tatsache, dass beispielsweise eine Bestellung storniert wurde, lässt sich nicht ungeschehen machen, aber der Effekt der Stornierung lässt sich unter Umständen durch eine Gegentransaktion kompensieren.

Das gleiche Muster findet sich auch im Kontext eines Girokontos, bei dem eine fehlgeschlagene Überweisung nicht ungeschehen gemacht werden kann – der Effekt der Abbuchung von der Bank aber durch eine Gutschrift wieder ausgeglichen wird.

Aus technischer Sicht ähnelt ein Domain-Event einem Command insofern, als es ebenfalls über einen Namen, Daten und Metadaten verfügt. Anders als beim Command wird der Name aber häufig durch ein Substantiv und ein Verb in der Vergangenheitsform ausgedrückt, beispielsweise "Bestellung storniert", um auszudrücken, dass es bereits passiert ist.

Ein Command und ein Domain-Event müssen dabei nicht zwingend in einer 1:1-Relation zueinander stehen. Es ist durchaus denkbar, dass ein Command unterschiedliche Domain-Events auslöst oder dass ein und dasselbe Domain-Event von verschiedenen Commands verursacht wird. Entscheidend dabei ist lediglich, dass ein Command stets mindestens ein Domain-Event nach sich zieht, sofern es nicht schon initial aus technischen Gründen abgelehnt wurde.

Ein Domain-Event dient aber nicht nur als Reaktion auf ein Command, sondern kann auch dazu genutzt werden, andere potenziell interessierte Parteien über fachlich relevante Ereignisse zu informieren. In diesem Zusammenhang kommt man bei der Implementierung dann häufig mit Messaging und Event-basierten Architekturen in Berührung.

Domain-Events in Domain-Driven Design (DDD)

State

Obwohl Commands und Domain-Events jeweils fachliche Daten enthalten, sind das nicht zwingend alle Daten, die für das Verarbeiten eines Commands relevant sind. Teilweise werden Daten benötigt, die Command-unabhängig sind und deren Lebensdauer die des (kurzlebigen) Commands überdauern. Als Beispiel bei der Stornierung einer Bestellung könnte die Informationen dienen, wann die Bestellung ursprünglich aufgegeben wurde, da eine Stornierung nur innerhalb von 14 Tagen möglich sein soll.

Derartige Informationen bilden den "State" einer Anwendung, repräsentieren also den aktuellen Zustand aller fachlichen Objekte. Commands verändern den State, Domain-Events informieren über dessen Veränderung. Unter Umständen entstehen jedoch auch Domain-Events, obwohl der State nicht verändert wurde, weil auch eine Nichtveränderung eine fachlich relevante Information sein kann – beispielsweise das Ablehnen eines Stornierungswunschs, worauf der Kundenservice reagieren soll.

Eine wichtige Anforderung an den State ist, dass er stets konsistent, verlässlich und korrekt ist. Command müssen daher atomar ablaufen, die Konsistenz wahren, dürfen sich nicht gegenseitig in die Quere kommen und müssen dauerhafte Änderungen nach sich ziehen. Sie folgen also den klassischen ACID-Kriterien, wie man sie aus relationalen Datenbanken kennt. Es obliegt der Anwendung, diese Kriterien im Zusammenspiel von Commands, Domain-Events und State zu gewährleisten.

State in Domain-Driven Design (DDD)

Aggregates

Genau diesen Zweck erfüllen in DDD die sogenannten "Aggregates". Ein Aggregate ist letztlich nichts anderes als eine Hülle, die fachlich und logisch zusammengehörigen State mit den entsprechenden Commands und Domain-Events kapselt, Commands bei Bedarf serialisiert und gegebenenfalls auftretende Konflikte auflöst.

Dabei ist es wichtig zu wissen, dass sich ein Command stets auf nur genau ein Aggregate bezieht. Es gibt also keine Aggregate-übergreifenden Commands, was aus transaktionaler Sicht auch nicht sinnvoll wäre, da jedes einzelne Aggregate ja für die Einhaltung der ACID-Kriterien in seinem Inneren zuständig ist. Aus dem Grund wird in DDD gelegentlich auch davon gesprochen, dass Aggregates eine "transaktionale Grenze" darstellen.

Die Frage, die sich daraus ergibt, lautet, wie man Aggregates idealerweise schneiden sollte. Große Aggregates verhindern fachliche Konflikte, schaden aber der Nebenläufigkeit der Anwendung, da Commands gegebenenfalls serialisiert werden müssen. Kleine Aggregates fördern ebendiese Nebenläufigkeit, führen aber rasch zu fachlichen Konflikten. Die Wahrheit liegt deshalb in der Mitte: Aggregates sollten so groß wie nötig, aber so klein wie möglich sein.

In dieser Hinsicht effizientes Aggregate-Design zu betreiben, ist eine der größten Herausforderungen bei DDD, aber zugleich auch eine der größten Chancen: Genau hier geht es nämlich um die Beschäftigung mit potenziellen fachlichen und geschäftlichen Risiken, deren Wahrscheinlichkeit und Gefahr. Dabei gilt es nicht, jegliches denkbare fachliche Probleme mit technischen Mitteln zu lösen, vielmehr gilt es, Aufwand und Risiken gegeneinander abzuwägen.

Es gibt durchaus Probleme, die aus technischer Sicht ungelöst bleiben können, die sich im Fall des Falles besser von Hand lösen lassen. Herauszufinden, welche Probleme das sind und welche Auswirkungen das hat, ist einer der wichtigsten Faktoren beim Entwurf von Aggregates.

Aggregates in Domain-Driven Design (DDD)

Fazit

Commands, Domain-Events, State und Aggregates – mehr braucht es im ersten Schritt aus technischer Sicht nicht, um mit DDD loslegen zu können, obwohl DDD natürlich noch weitaus mehr umfasst. Der springende Punkt ist dabei aber nicht, diese Werkzeuge gemäß Lehrbuch anzuwenden, sondern sie als Mittel zum Zweck zu begreifen. Das Hauptaugenmerk sollte daher auf dem eigentlichen Sinn von DDD liegen, dem Finden einer gemeinsamen und fachlichen Sprache in einem interdisziplinären Team.

Die Tatsache, dass diese Herausforderung praktisch nichts mit Technologie zu tun hat, dürfte einer der Hauptfaktoren sein, warum DDD seit knapp 20 Jahren nicht den Weg in den Mainstream findet. DDD ernsthaft anzuwenden bedeutet nämlich, als Entwicklerin oder Entwickler die technische Komfortzone zu verlassen und sich in neues, gänzlich unbekanntes Terrain vorzuwagen. Das erfordert Mut, Neugier, Interesse und Empathie.

Wer diese Eigenschaften mitbringt, hält jedoch die wichtigsten Werkzeuge in der Hand, um DDD erfolgreich anwenden zu können. Wer DDD hingegen auf die Muster aus dem Buch von Eric Evans reduziert, befolgt DDD zwar formal, verfehlt das eigentliche Ziel aber bei Weitem. Letztlich verhält es sich bei DDD wie bei den berühmten objektorientierten Entwurfsmustern der Gang of Four: Es geht nämlich nicht um die beispielhafte Implementierung, sondern um die grundlegende Idee dahinter – und die wird hier wie dort allzu oft verkannt.