Freigeben über


Entwerfen eines Microservice-Domänenmodells

Tipp

Dieser Inhalt ist ein Auszug aus dem eBook .NET Microservices Architecture for Containerized .NET Applications, verfügbar auf .NET Docs oder als kostenlose herunterladbare PDF, die offline gelesen werden kann.

.NET Microservices-Architektur für containerisierte .NET-Anwendungen eBook-Cover-Thumbnail.

Definieren Sie ein umfassendes Domänenmodell für jeden Business-Microservice oder gebundenen Kontext.

Ihr Ziel ist es, ein einzelnes einheitliches Domänenmodell für jeden Business Microservice oder Bounded Context (BC) zu erstellen. Beachten Sie jedoch, dass ein BC- oder Business Microservice manchmal aus mehreren physischen Diensten besteht, die ein einzelnes Domänenmodell gemeinsam nutzen. Das Domänenmodell muss die Regeln, Das Verhalten, die Geschäftssprache und die Einschränkungen des einzelnen gebundenen Kontexts oder des geschäftsständlichen Microservice erfassen, den es darstellt.

Das Domänenentitätsmuster

Entitäten stellen Domänenobjekte dar und werden in erster Linie durch ihre Identität, Kontinuität und Persistenz im Laufe der Zeit und nicht nur durch die Attribute definiert, aus denen sie bestehen. Wie Eric Evans sagt, "ein Objekt, das in erster Linie durch seine Identität definiert wird, wird als Entität bezeichnet." Entitäten sind im Domänenmodell sehr wichtig, da sie die Basis für ein Modell sind. Daher sollten Sie sie sorgfältig identifizieren und entwerfen.

Die Identität einer Entität kann mehrere Mikroservices oder gebundene Kontexte überschreiten.

Die gleiche Identität (d. h. derselbe Id Wert, obwohl möglicherweise nicht dieselbe Domänenentität) kann über mehrere gebundene Kontexte oder Microservices modelliert werden. Das bedeutet jedoch nicht, dass dieselbe Entität mit denselben Attributen und derselben Logik in mehreren gebundenen Kontexten implementiert werden würde. Stattdessen beschränken Entitäten in jedem gebundenen Kontext ihre Attribute und Verhaltensweisen auf diejenigen, die in der Domäne des Gebundenen Kontexts erforderlich sind.

Beispielsweise verfügt die Käuferentität möglicherweise über die meisten Attribute einer Person, die in der Benutzerentität im Profil- oder Identitäts-Microservice definiert sind, einschließlich der Identität. Die Käuferentität im Bestell-Microservice hat jedoch möglicherweise weniger Attribute, da nur bestimmte Käuferdaten mit dem Bestellvorgang zusammenhängen. Der Kontext der einzelnen Microservice- oder Bounded Context-Elemente wirkt sich auf das Domänenmodell aus.

Domänenentitäten müssen das Verhalten zusätzlich zur Implementierung von Datenattributen implementieren.

Eine Domänenentität in DDD muss die Domänenlogik oder das Verhalten im Zusammenhang mit den Entitätsdaten implementieren (das Objekt, auf das im Speicher zugegriffen wird). Beispielsweise müssen Sie als Teil einer Auftragsentitätsklasse Geschäftslogik und -vorgänge als Methoden für Aufgaben implementiert haben, z. B. das Hinzufügen eines Auftragselements, die Datenüberprüfung und die Gesamtberechnung. Die Methoden der Entität kümmern sich um die Invarianten und Regeln der Entität, anstatt diese Regeln über die Anwendungsebene verteilt zu haben.

Abbildung 7-8 zeigt eine Domänenentität, die nicht nur Datenattribute, sondern Vorgänge oder Methoden mit zugehöriger Domänenlogik implementiert.

Diagramm mit dem Muster einer Domänenentität.

Abbildung 7-8. Beispiel für ein Domänenentitätsdesign, das Daten plus Verhalten implementiert

Eine Domänenmodellentität implementiert Verhaltensweisen über Methoden, d. h. es handelt sich nicht um ein "anemic"-Modell. Natürlich können Sie entitäten haben, die keine Logik als Teil der Entitätsklasse implementieren. Dies kann in untergeordneten Entitäten innerhalb eines Aggregats geschehen, wenn die untergeordnete Entität keine spezielle Logik aufweist, da der Großteil der Logik im Aggregatstamm definiert ist. Wenn Sie über einen komplexen Microservice verfügen, der logik in den Dienstklassen und nicht in den Domänenentitäten implementiert ist, können Sie in das anemische Domänenmodell fallen, das im folgenden Abschnitt erläutert wird.

Rich-Domänenmodell im Vergleich zum anemischen Domänenmodell

In seinem Beitrag AnemicDomainModel beschreibt Martin Fowler auf diese Weise ein anemisches Domänenmodell:

Das grundlegende Merkmal eines anämischen Domänenmodells ist, dass es auf den ersten Blick täuschend echt aussieht. Es gibt Objekte, viele benannt nach den Substantiven im Domänenbereich, und diese Objekte sind mit den umfangreichen Beziehungen und der Struktur verbunden, die echte Domänenmodelle haben. Wenn Sie jedoch das Verhalten betrachten, stellen Sie fest, dass es für diese Objekte kaum Verhaltensweisen gibt, wodurch sie letztlich nur aus Gettern und Settern bestehen.

Wenn Sie ein anemisches Domänenmodell verwenden, werden diese Datenmodelle natürlich aus einer Reihe von Dienstobjekten (traditionell als Geschäftsebene bezeichnet) verwendet, die alle Domänen- oder Geschäftslogik erfassen. Die Geschäftsebene liegt über dem Datenmodell und verwendet das Datenmodell ausschließlich als Daten.

Das anemische Domänenmodell ist nur ein prozeduraler Stilentwurf. Anämische Entitätsobjekte sind keine echten Objekte, weil ihnen das Verhalten (Methoden) fehlt. Sie enthalten nur Dateneigenschaften und somit kein objektorientiertes Design. Indem Sie das gesamte Verhalten in Dienstobjekte (die Geschäftsebene) umsetzen, enden Sie im Wesentlichen mit Spaghetti-Code oder Transaktionsskripts, und daher verlieren Sie die Vorteile, die ein Domänenmodell bietet.

Unabhängig davon, ob Ihr Mikroservice oder gebundener Kontext sehr einfach ist (ein CRUD-Dienst), ist das anemische Domänenmodell in Form von Entitätsobjekten mit nur Dateneigenschaften möglicherweise gut genug, und es lohnt sich möglicherweise nicht, komplexere DDD-Muster zu implementieren. In diesem Fall ist es einfach ein Persistenzmodell, da Sie absichtlich eine Entität mit nur Daten für CRUD-Zwecke erstellt haben.

Aus diesem Grund eignen sich Microservices-Architekturen perfekt für einen multiarchitektonischen Ansatz, abhängig von jedem konkreten Kontext. In eShopOnContainers implementiert der Sortier-Microservice beispielsweise DDD-Muster, aber der Katalog-Microservice, der ein einfacher CRUD-Dienst ist, nicht.

Einige Leute sagen, dass das anemische Domänenmodell ein Antimuster ist. Es hängt wirklich davon ab, was Sie implementieren. Wenn der von Ihnen erstellten Microservice einfach genug ist (z. B. ein CRUD-Dienst), ist es nach dem anemischen Domänenmodell kein Antimuster. Wenn Sie jedoch die Komplexität der Domäne eines Microservice mit vielen sich ständig ändernden Geschäftsregeln bewältigen müssen, kann das anemische Domänenmodell ein Antimuster für diesen Mikroservice oder gebundenen Kontext sein. In diesem Fall kann das Entwerfen als umfangreiches Modell mit Entitäten, die Daten plus Verhalten enthalten, sowie die Implementierung zusätzlicher DDD-Muster (Aggregate, Wertobjekte usw.) große Vorteile für den langfristigen Erfolg eines solchen Microservice haben.

Weitere Ressourcen

Das Value-Objektmuster

Wie Eric Evans bemerkt hat, "Viele Objekte haben keine konzeptionelle Identität. Diese Objekte beschreiben bestimmte Merkmale eines Dings."

Eine Entität erfordert eine Identität, aber es gibt viele Objekte in einem System, die dies nicht tun, wie das Value-Objekt-Muster. Ein Wertobjekt ist ein Objekt ohne konzeptionelle Identität, das einen Domänenaspekt beschreibt. Hierbei handelt es sich um Objekte, die Sie instanziieren, um Designelemente darzustellen, die sie nur vorübergehend betreffen. Sie kümmern sich um das, was sie sind, nicht wer sie sind. Beispiele sind Zahlen und Zeichenfolgen, können aber auch Konzepte auf höherer Ebene wie Gruppen von Attributen sein.

Etwas, das eine Entität in einem Microservice ist, ist möglicherweise keine Entität in einem anderen Mikroservice, da der gebundene Kontext im zweiten Fall eine andere Bedeutung haben könnte. Beispielsweise hat eine Adresse in einer E-Commerce-Anwendung möglicherweise überhaupt keine Identität, da sie nur eine Gruppe von Attributen des Kundenprofils für eine Person oder ein Unternehmen darstellt. In diesem Fall sollte die Adresse als Wertobjekt klassifiziert werden. In einer Anwendung für ein Stromversorgerunternehmen könnte die Kundenadresse jedoch für die Geschäftsdomäne wichtig sein. Daher muss die Adresse über eine Identität verfügen, damit das Abrechnungssystem direkt mit der Adresse verknüpft werden kann. In diesem Fall sollte eine Adresse als Domänenentität klassifiziert werden.

Eine Person mit einem Namen und Nachnamen ist in der Regel eine Entität, da eine Person identitätsberechtigt ist, auch wenn der Name und Nachname mit einem anderen Wertesatz übereinstimmen, z. B. wenn diese Namen auch auf eine andere Person verweisen.

Wertobjekte sind in relationalen Datenbanken und ORMs wie Entity Framework (EF) schwer zu verwalten, während sie in dokumentorientierten Datenbanken einfacher zu implementieren und zu verwenden sind.

EF Core 2.0 und höhere Versionen enthalten das Feature "Owned Entities ", das das Behandeln von Wertobjekten erleichtert, wie wir später ausführlich sehen werden.

Weitere Ressourcen

Das Aggregatmuster

Ein Domänenmodell enthält Cluster verschiedener Datenentitäten und Prozesse, die einen erheblichen Funktionalitätsbereich steuern können, z. B. Auftragserfüllung oder Bestand. Eine differenziertere DDD-Einheit ist das Aggregat, das einen Cluster oder eine Gruppe von Entitäten und Verhaltensweisen beschreibt, die als zusammenhängende Einheit behandelt werden können.

Normalerweise definieren Sie ein Aggregat basierend auf den benötigten Transaktionen. Ein klassisches Beispiel ist ein Auftrag, der auch eine Liste von Bestellpositionen enthält. Ein Bestellelement ist in der Regel eine Entität. Im Bestellaggregat, das auch die Bestellentität als Stammentität enthält, handelt es sich dabei jedoch um eine untergeordnete Entität, die in der Regel als Aggregatstamm bezeichnet wird.

Das Identifizieren von Aggregaten kann schwierig sein. Ein Aggregat ist eine Gruppe von Objekten, die konsistent sein müssen, aber Sie können nicht nur eine Gruppe von Objekten auswählen und sie als Aggregat bezeichnen. Sie müssen mit einem Domänenkonzept beginnen und über die Entitäten nachdenken, die in den am häufigsten verwendeten Transaktionen im Zusammenhang mit diesem Konzept verwendet werden. Diese Entitäten, die transaktional konsistent sein müssen, sind die Formen eines Aggregats. Das Denken an Transaktionsvorgänge ist wahrscheinlich die beste Möglichkeit, Aggregate zu identifizieren.

Das Aggregatstamm- oder Stammentitätsmuster

Ein Aggregat besteht aus mindestens einer Entität: dem Aggregatstamm, auch als Stammentität oder primäre Entität bezeichnet. Darüber hinaus können mehrere untergeordnete Entitäten und Wertobjekte vorhanden sein, wobei alle Entitäten und Objekte zusammenarbeiten, um erforderliche Verhaltensweisen und Transaktionen zu implementieren.

Der Zweck eines Aggregatstamms besteht darin, die Konsistenz des Aggregats sicherzustellen; Es sollte der einzige Einstiegspunkt für Aktualisierungen des Aggregats über Methoden oder Vorgänge in der Aggregatstammklasse sein. Sie sollten Änderungen an Entitäten innerhalb des Aggregats nur über den Aggregatstamm vornehmen. Es handelt sich um den Konsistenzschutz des Aggregats, wobei alle invarianten und Konsistenzregeln berücksichtigt werden, die Sie möglicherweise in Ihrem Aggregat einhalten müssen. Wenn Sie eine untergeordnete Entität oder ein untergeordnetes Wertobjekt unabhängig ändern, kann der Aggregatstamm nicht sicherstellen, dass sich das Aggregat in einem gültigen Zustand befindet. Dies wäre vergleichbar mit einem Tisch mit losem Tischbein. Die Aufrechterhaltung der Konsistenz ist der Hauptzweck des Aggregatstamms.

In Abbildung 7-9 können Sie Beispielaggregate wie das Käuferaggregat anzeigen, das eine einzelne Entität (den Aggregatstammkäufer) enthält. Das Bestellaggregat enthält mehrere Entitäten und ein Wertobjekt.

Diagramm, das ein Käuferaggregat und ein Bestellaggregat vergleicht.

Abbildung 7-9. Beispiel für Aggregate mit mehreren oder einzelnen Entitäten

Ein DDD-Domänenmodell besteht aus Aggregaten, ein Aggregat kann nur eine Entität oder mehrere haben und auch Wertobjekte enthalten. Beachten Sie, dass das Aggregat „Buyer“ (Käufer) über zusätzliche untergeordnete Entitäten verfügen könnte. Dies hängt von Ihrer Domäne ab, wie auch beim Microservice „Ordering“ (Bestellung) in der eShopOnContainers-Referenzanwendung. Abbildung 7-9 veranschaulicht nur einen Fall, in dem der Käufer eine einzelne Entität hat, als Beispiel für ein Aggregat, das nur einen Aggregatstamm enthält.

Um die Trennung von Aggregaten aufrechtzuerhalten und klare Grenzen zwischen ihnen zu behalten, empfiehlt es sich, in einem DDD-Domänenmodell die direkte Navigation zwischen Aggregaten und nur das Fremdschlüsselfeld (FK) zu verbieten, wie im Domänenmodell "Order microservice" in eShopOnContainers implementiert. Die Bestellentität verfügt wie im folgenden Code dargestellt nur über ein Fremdschlüsselfeld für den Käufer, jedoch über keine EF Core-Navigationseigenschaft:

public class Order : Entity, IAggregateRoot
{
    private DateTime _orderDate;
    public Address Address { get; private set; }
    private int? _buyerId; // FK pointing to a different aggregate root
    public OrderStatus OrderStatus { get; private set; }
    private readonly List<OrderItem> _orderItems;
    public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
    // ... Additional code
}

Das Identifizieren und Arbeiten mit Aggregaten erfordert Forschung und Erfahrung. Weitere Informationen finden Sie in der folgenden Liste zusätzlicher Ressourcen.

Weitere Ressourcen