2024-08-09

Spaceteams Coding Workshop: Wie man Domain Driven Design zum Leben erweckt

Software Engineering
Learning & Growth
showing a striking conceptual design featuring a gritty urban inspired illustration of a laptop with coding on
showing badge with astronauts representing a software engineer team profile image

GESCHRIEBEN VON

Hung, Tawhi & Saad

INHALT

Während eines Workshops in unserer Spacetime haben wir Domain Driven Design (DDD) und dessen praktische Anwendung kennengelernt. DDD ist ein Ansatz zur Softwareentwicklung, der sich an den Geschäftsanforderungen orientiert. Wichtige Konzepte sind Domain Storytelling, Ubiquitous Language und Bounded Contexts. Der Workshop verwendete praktische Beispiele aus dem Bankwesen und einem detaillierten Leasing-Beispiel, um zu zeigen, wie architektonische Konzepte im Code umgesetzt werden können. Dies fördert verständlichen und wartbaren Code.

Dieser Artikel fokussiert sich auf einen der letzten Workshops, geleitet von Henning Schwentner, einem bekannten Experten in der DDD-Community.

DDD-Konzepte

Bevor wir zu DDD im Code kommen, schauen wir uns kurz einige DDD-Konzepte an.

Ubiquitous Language: Das ist eine gemeinsame Sprache für verschiedene Geschäftsprozesse oder Entities. Diese Sprache wird von den Domainexpert:innen verwendet und sollte daher auch im Code verwendet werden, z.B. in Variablennamen, Methoden und Klassen.

Bounded Contexts: Durch Domain Storytelling erhalten wir eine Visualisierung der Domain. Danach können klare Grenzen gezogen werden, um die Domain, die Software und die Teams zu organisieren. Diese Grenzen nennt man Bounded Contexts. Beispiel: Im Geschäftsfeld Autoleasing können wir alle Aktivitäten und Einträge im Zusammenhang mit dem Verkauf in einem Kontext und alle Aktivitäten und Einheiten im Zusammenhang mit dem Risikomanagement des Darlehensantrags in einem anderen Kontext stellen.

Domain Storytelling: Das ist eine Methode, um Domainwissen in Softwareanwendungen zu übertragen, die Geschäftsprobleme lösen. Domainexpert:innen und Entwickler:innen kommen zusammen und visualisieren den Geschäftsprozess, was bei der Entwicklung hilft, die Domain zu verstehen und die Software entsprechend zu entwickeln.

showing domainstory leasingninja example with contexts

Quelle: https://speakerdeck.com/hschwentner/

Bausteine von DDD:

  • Value Object: Ein unveränderliches Objekt, das nur durch seinen Wert definiert wird, z.B. eine IBAN für ein Bankkonto.
  • Entities: Veränderliche Objekte mit einer eindeutigen Identität innerhalb der Domain. Sie benötigen eine Identifikation zur Unterscheidung und haben einen Lebenszyklus, z.B. ein Bankkonto mit einer eindeutigen Kontonummer.
  • Aggregates: Aggregates sind Entitäten, die aus mehreren Entitäten bestehen können und innerhalb ihrer inneren Entitäten Konsistenz und Integrität sicherstellen.
showing an aggregation of entities

Quelle: https://speakerdeck.com/hschwentner/

  • Repository: Repositories dienen als Zugangspunkte zu Aggregates und verbergen die technischen Details der Infrastruktur. Sie erleichtern die Zuordnung externer Daten zu Entitäten und Value Objects und speichern Aggregates.
showing repository with aggregations

Quelle: https://speakerdeck.com/hschwentner/

  • Domain Services: Wenn Geschäftslogik nicht natürlich in den Kontext von Entitäten, Value Objects oder Aggregates passt, wird sie als Domain Service implementiert.

Nun, da wir die Bausteine von DDD kennen, widmen wir uns einigen Beispielen, die wir während des Workshops bearbeitet haben. Diese Beispiele helfen uns, die Konzepte von DDD in der Praxis besser zu verstehen und zu visualisieren. Wir beginnen mit einem Beispiel für DDD im Bankwesen.

Beispiel aus dem Bankwesen mit xMolecules

In DDD ist eine Entität eine Darstellung eines eindeutigen und identifizierbaren realen Objekts. In unserem Bankbeispiel passt ein Konto perfekt zu diesem Konzept, da es eine eindeutige Kennung wie eine Kontonummer und einen Zustand wie den Kontostand besitzt.

Die Konto-Entity repräsentiert ein einzigartiges und identifizierbares Objekt in der Bankdomain, und sein Zustand wird durch die Methoden Einzahlung und Auszahlung verwaltet. Diese Methoden setzen Domainregeln durch, z.B. dass Transaktionen nicht negativ sein dürfen und ausreichend Geld vorhanden sein muss, um Auszahlungen zu decken. Durch die Anwendung von DDD-Prinzipien schafft das Bankbeispiel ein Softwaremodell, das mit der Geschäftsdomain übereinstimmt und verständlichen und wartbaren Code fördert.

Traditionell ist es üblich, den Kontostand vor einer Auszahlung zu überprüfen, um sicherzustellen, dass genügend Geld vorhanden ist. Wir haben jedoch einen anderen Ansatz gewählt, indem wir eine Ausnahme ausgelöst haben. Dies kapselt nicht nur Fehlermeldungen für einfacheres Debugging, sondern erfordert auch, dass die Aufrufer Fehler explizit behandeln.

Design by Contract (DbC) ist eine Methode zur Softwareentwicklung, die die Definition klarer und formaler Verträge zwischen den Komponenten eines Softwaresystems betont. Diese Verträge spezifizieren die Verpflichtungen der einzelnen Komponenten und stellen sicher, dass das System wie erwartet funktioniert.

Beispiel:

showing assertion using keyword assert

Quelle: https://speakerdeck.com/hschwentner/

Die Assertion assert amount <= balance() ist eine Möglichkeit zu überprüfen, ob die Vorbedingung der Methode withdraw() erfüllt ist. Die Vorbedingung besagt, dass der Betrag, der abgehoben werden soll, kleiner oder gleich dem Kontostand sein muss. Wenn die Assertion fehlschlägt, wird eine Ausnahme ausgeworfen.

  • Dies ist eine Möglichkeit, einen Vertrag zwischen der Methode withdraw() und ihrem / ihrer Aufrufer:in zu spezifizieren. Der Vertrag besagt, dass der / die Aufrufer:in nicht mehr Geld abheben darf, als auf dem Konto vorhanden ist. Dieser Vertrag hilft sicherzustellen, dass das System wie erwartet funktioniert.

Im weiteren Verlauf unseres Bankbeispiels haben wir verschiedene Anmerkungen aus xMolecules untersucht, die es ermöglichen, architektonische Konzepte im Code auszudrücken.

  • Mit diesen Tools können Sie spezielle Labels und Datentypen verwenden, um zu beschreiben, wie verschiedene Teile Ihrer Softwarearchitektur zusammenpassen. Diese Tools generieren auch automatisch einige grundlegende Codes, was es einfacher macht, Ihre architektonischen Ideen mit dem tatsächlichen Code zu verbinden, den Sie schreiben.

In diesem Beispiel zeigt die Annotation @Entity an, dass die Klasse Account eine Entität ist. Die Annotation @Identity zeigt an, dass die Eigenschaft id der Primärschlüssel der Entität ist. Die Verwendung der Annotation ist eine Möglichkeit, DDD-Prinzipien im Code zu implementieren. Dies trägt dazu bei, dass das Domainmodell in einer Weise dargestellt wird, die mit den DDD-Prinzipien übereinstimmt. Hier sind einige Vorteile der Verwendung der Annotation:

  • Verbesserte Lesbarkeit des Codes: Die Annotation macht es dem Entwicklerteam klar, dass eine Klasse eine Entität ist. Dies kann die Lesbarkeit des Codes verbessern.
  • Reduzierter Boilerplate-Code: Die Annotation kann verwendet werden, um die Menge an Boilerplate-Code zu reduzieren, die erforderlich ist, um Entitäten in einer Datenbank zu speichern.
  • Erhöhte Wartbarkeit: Die Verwendung der Annotation kann den Code wartbarer machen, indem klar gemacht wird, wie Entitäten auf Datenbanktabellen abgebildet werden.
showing value object

Quelle: https://speakerdeck.com/hschwentner/

Leasing Ninja Beispiel

Danach haben wir uns ein detaillierteres Beispiel angesehen, nämlich das Leasing Ninja Beispiel, ein Open Source DDD-Beispiel, das hier verfügbar ist.

Die Domäne:

Unsere Domäne für dieses Beispiel ist das Leasing eines Autos: Ein:e Kund:in geht zu einem Autohaus und fordert ein Auto an, der / die Verkäufer:in berechnet die monatliche Rate und erstellt einen Vertrag. Nachdem der / die Kund:in den Vertrag unterschrieben hat, gibt der / die Verkäufer:in den Vertrag an die Risikoabteilung weiter, die den Vertrag prüft. Wenn alles gut aussieht, wird der / die Verkäufer:in informiert und kann die Autoschlüssel übergeben.

Für das Beispiel konzentrieren wir uns hauptsächlich auf den "Sales"-Kontext und den "Risk Management"-Kontext.

Nachdem wir unsere Bausteine und Bounded Contexts mit Domain Storytelling identifiziert haben, gehen wir zum Code über und:

  • machen Klassen aus unseren Arbeitsobjekten (Nomen)
  • machen Methoden/Befehle aus unseren Aktivitäten (Verben)

Der Code:

Wir begannen mit einem Überblick über die Java-Implementierung des Beispiels, verfügbar hier:

https://github.com/leasingninja/leasingninja-java-boundedcontexts-domainmodel

Dann verglichen wir einige Teile des Codes mit anderen Implementierungen, die für uns interessant sind, wie z.B. die Verwendung von Typescript oder andere Wege wie die funktionale Programmierung mit F#.

Der Code dieser Beispiele war sehr lesbar, da er mit den DDD-Bausteinen strukturiert ist und auch die xMolecules-Bibliothek für Anmerkungen verwendet.

Hier sind einige Codebeispiele von DDD-Bausteinen aus dem Sales-Kontext. Diese Bausteine sind wie folgt annotiert:

Car Value Object:

@ValueObject
public record Car(String car) {

    public static Car of(String car) {
        return new Car(car);
    }

}

Contract Entity:


@Entity
public class Contract {

  @Identity
  private final ContractNumber number;

    private final Customer lessee;
    private final Car car;
    private final Amount price;

    private record Calculation(LeaseTerm leaseTerm, Interest interest, Amount installment) {}
  private Optional<Calculation> calculation;

  private Optional<SignDate> signDate;
  ...
}

Contract Repository:

@Repository
public interface Contracts {

    Contract with(ContractNumber number);

    void save(Contract contract);

}

FinancialCalculator Service (simuliert den berühmten HP12c calculator):

@Service
public class FinancialCalculator {

    public static double pmt(double n, double iInPercent, double pv, double fv, double s) {
        double i = iInPercent / 100.0;

        return pmtWithDecimalInterestRate(n, i, pv, fv, s);
    }
  ...
}

Zusammenfassung

Bei Spaceteams glauben wir, dass DDD wichtig ist. Wir denken, dass es entscheidend ist, dass alle unsere Entwickler:innen die Geschäftslogik in unseren Projekten verstehen und mit den Stakeholder:innen sprechen können, um echten Mehrwert für die Kunden zu schaffen.

Während des Workshops haben wir die Vorteile von Domain Driven Design (DDD) kennengelernt. Einer der Hauptvorteile von DDD ist die klare Trennung zwischen der Domain und den Implementierungsdetails. Dies ermöglicht es uns, uns auf das Verständnis der Domain zu konzentrieren und die richtige Lösung für das Geschäft zu schaffen.

Im Workshop haben wir die praktischen Aspekte von DDD im Code untersucht. Wir haben gelernt, wie man DDD-Anmerkungen mit xMolecules anwendet, was uns hilft, architektonische Konzepte im Code auszudrücken und unsere architektonischen Ideen mit dem tatsächlichen Code zu verbinden. Außerdem haben wir etwas über das Design by Contract gelernt, eine Methode, die betont, klare und formale Verträge zwischen den Komponenten eines Softwaresystems zu definieren, um sicherzustellen, dass das System wie erwartet funktioniert.

Insgesamt war der Workshop sehr hilfreich, da er uns Codebeispiele lieferte, die uns halfen, die Konzepte von DDD besser zu verstehen. Unsere Softwareteams hatten diesen Workshop sehnlichst erwartet, da sie viele DDD-Workshops hatten und endlich die Konzepte im Code und deren Umsetzung sehen konnten.