DDD & Co., Teil 6: Vom Modell zum Code

Die Domäne ist modelliert, gespeichert werden Events. Wie lässt sich das Ganze nun von der Theorie in die Praxis überführen? Wie könnte Code aussehen, der die fachliche Modellierung widerspiegelt? Ein Entwurf.

In Pocket speichern vorlesen Druckansicht 2 Kommentare lesen
Lesezeit: 4 Min.
Von
  • Golo Roden
Inhaltsverzeichnis

Die Domäne ist modelliert, gespeichert werden Events. Wie lässt sich das Ganze nun von der Theorie in die Praxis überführen? Wie könnte Code aussehen, der die fachliche Modellierung widerspiegelt? Ein Entwurf.

Das Ergebnis der vierten Folge war eine Modellierung der Domäne TodoMVC. Die übrigen Konstrukte wie begrenzte Kontexte, Aggregate, Kommandos und fachliche Ereignisse sind in der Domäne angesiedelt, weshalb zunächst ein gemeinsamer Rahmen zu schaffen ist.

Mehr Infos

Das lässt sich auf einfache Art erledigen, indem man ein neues Verzeichnis namens todomvc anlegt. Mehr ist tatsächlich nicht erforderlich, da die Domäne an sich keine eigene Logik ausführt.

Das Gleiche gilt für den begrenzten Kontext planning, der deshalb als Verzeichnis unterhalb von todomvc anzulegen ist.

Innerhalb dieses Kontexts wird es interessanter. Hier befindet sich das Aggregat todo, das für die fachliche Logik der einzelnen Aufgaben zuständig ist. Deshalb ist hier als nächstes die Datei todo.js anzulegen (selbstverständlich kann dafür auch eine andere Programmiersprache als JavaScript zum Einsatz kommen, das Konzept lässt sich übertragen).

In der Datei todo.js kann man nun die Kommandos und fachlichen Ereignisse anlegen. Es bietet sich an, dazu zwei Abschnitte zu definieren, die anschließend exportiert werden:

const commands = {};
const events = {};

module.exports = { commands, events };

Die einzelnen Kommandos und fachlichen Ereignisse werden als Funktionen hinterlegt. Da Kommandos eventuell asynchronen Code enthalten, benötigen sie einen Callback. Außerdem brauchen sie Zugriff auf das Aggregat und die im Kommando enthaltenen Daten. Damit ergibt sich eine Signatur wie im folgenden Beispiel:

const commands = {
note (todo, data, done) {
// ...
}
};

Die Funktion muss prüfen, ob ein Titel für die Aufgabe übergeben wurde und im Erfolgsfall ein noted-Ereignis veröffentlichen:

const commands = {
note (todo, data, done) {
if (!data.title) {
return done(new Error('Title is missing.'));
}

todo.events.publish('noted', {
title: data.title
});

done(null);
}
};

Um die Lesbarkeit des Codes zu verbessern, kann die technische Formulierung eines done-Callbacks mit einem Error-Objekt durch eine sprechendere Formulierung ersetzt werden. Außerdem können Kommandos zusätzlichen zu den eigentlichen Daten auch Metadaten enthalten, weshalb es sich anbietet, den Parameter entsprechend zu ändern:

const commands = {
note (todo, command, mark) {
if (!command.data.title) {
return mark.asRejected('Title is missing.');
}

todo.events.publish('noted', {
title: command.data.title;
});

mark.asDone();
}
};

Nachdem das Kommando implementiert wurde, muss das Aggregat auch auf fachliche Ereignisse reagieren. Ereignisse führen zu einer Änderung des Zustands des Aggregats, was sich angelehnt an React wie folgt ausdrücken lässt:

const events = {
noted (todo, event) {
todo.setState({
title: event.data.title
});
}
};

Da die Zustandsänderung stets synchron erfolgt, kann an der Stelle auf einen Callback verzichtet werden. Ansonsten gelten die gleichen Überlegungen für die Parameter wie bei den Kommandos.

Das Verwenden des Zustands wirft die Frage auf, wie er sich initialisieren lässt. Dazu bietet sich neben commands und events ein weiterer Abschnitt namens initialState an, der ebenfalls zu exportieren ist:

const initialState = {
title: ''
};

const commands = {
// ...
};

const events = {
// ...
};

module.exports = { initialState, commands, events };

Auf die gleiche Art lassen sich auch alle übrigen Kommandos und fachlichen Ereignisse definieren. Das Vorgehen zeigt außerdem gut, welcher gemeinsame Zustand von dem Aggregat verwaltet wird – es lässt sich daher gut erkennen, wie die Transaktionsgrenzen verlaufen, was wiederum für die Modellierung der Domäne sehr wichtig ist.

Der bislang erarbeitete Code stellt noch keine ausführbare Anwendung dar. Stattdessen handelt es sich dabei zunächst nur um eine Beschreibung der Domäne in JavaScript.

Tatsächlich benötigt das Aggregat jedoch keinen weiteren Code, da alle weiteren Aspekte wie der Zugriff auf den Event-Store oder eine vorgeschaltete REST- oder WebSocket-API von Außen ergänzt werden können.

Damit bleibt der Code sehr nah an der fachlichen Modellierung und ist, mit gewissen Abstrichen, auch für einen Fachexperten les- und vor allem bewertbar.

tl;dr: Das vorherige Modellieren einer Domäne mit DDD hilft, entsprechenden Code zu schreiben, der die Fachlichkeit klar ausdrückt und andere Aspekte außen vor lässt. Allerdings entsteht auf dem Weg zunächst nur eine Beschreibung der Domäne in JavaScript, für eine ausführbare Anwendung fehlt noch das technische Fundament. ()