Entwerfen von Microservices im Rahmen der taktischen DDD-Phase
Domain-driven design (DDD) widersetzt sich der Idee, ein einzelnes einheitliches Modell für das gesamte System zu haben. Stattdessen empfiehlt es sich, das System in begrenzungsgebundene Kontexte zu unterteilen, jeweils mit einem eigenen Modell. Während der strategischen Phase von DDD definieren Sie die Geschäftsdomäne und definieren gebundene Kontexte für Ihre Domänenmodelle.
In der taktischen DDD-Phase definieren Sie Ihre Domänenmodelle mit größerer Genauigkeit. Die taktischen Muster werden nur in einem Kontextgrenzenbereich angewendet. In einer Microservices-Architektur, bei der jeder gebundene Kontext ein Mikroservicekandidat ist, sind die Entitäts- und Aggregatmuster zu beachten. Wenn Sie diese Muster anwenden, können Sie natürliche Grenzen für die Dienste in Ihrer Anwendung identifizieren. Weitere Informationen finden Sie unter Identifizieren von Mikroservicegrenzen. Im Allgemeinen sollte ein Microservice nicht kleiner als ein Aggregat und nicht größer als ein gebundener Kontext sein.
Dieser Artikel überprüft die taktischen Muster und wendet sie dann auf den gebundenen Kontext des Versands in der Drone Delivery-Anwendung an.
Übersicht über die taktischen Muster
Dieser Abschnitt enthält eine kurze Zusammenfassung der taktischen DDD-Muster. Wenn Sie mit DDD vertraut sind, können Sie es überspringen. Diese Muster werden in Kapitel 5 und 6 des Buchs eric Evans und in der Implementierung von Domain-Driven Design von Vaughn Vernon ausführlicher beschrieben.
Entitäten: Eine Entität ist ein Objekt mit einer eindeutigen Identität, die bestehen bleibt. In einer Anwendung für Bankgeschäfte sind Kunden und Konten beispielsweise Entitäten.
Eine Entität verfügt im System über einen eindeutigen Bezeichner, mit dem die Entität nachgeschlagen bzw. abgerufen werden kann. Dies bedeutet nicht, dass der Bezeichner immer direkt für Benutzer verfügbar gemacht wird. Es kann sich um eine GUID oder einen Primärschlüssel in einer Datenbank handeln.
Eine Identität kann mehrere begrenzungsgebundene Kontexte umfassen und kann über die Lebensdauer der Anwendung hinaus bestehen. So sind z. B. Bankkontonummern oder vom Staat ausgestellte IDs nicht an eine bestimmte Anwendung gebunden.
Die Attribute einer Entität können sich im Laufe der Zeit ändern. Beispielsweise kann sich der Name oder die Adresse einer Person ändern, aber sie bleiben gleich.
Wertobjekte: Ein Wertobjekt hat keine Identität. Sie wird nur durch die Werte ihrer Attribute definiert. Wertobjekte sind unveränderlich. Zum Aktualisieren eines Wertobjekts wird eine neue Instanz erstellt, um die alte zu ersetzen. Wertobjekte können Methoden enthalten, die domänenlogik kapseln, aber diese Methoden dürfen keine Nebenwirkungen erzeugen oder den Zustand des Objekts ändern. Häufige Beispiele für Wertobjekte sind Farben, Datumsangaben und Uhrzeiten sowie Währungswerte.
Aggregate: Ein Aggregat definiert eine Konsistenzgrenze für eine oder mehrere Entitäten. Eine bestimmte Entität in einem Aggregat ist die Stammentität. Die Suche wird anhand des Bezeichners der Stammentität durchgeführt. Alle anderen Entitäten im Aggregat sind untergeordnete Elemente der Stammentität, und es wird darauf verwiesen, indem den Zeigern der Stammentität gefolgt wird.
Der Zweck eines Aggregats ist die Modellierung von Transaktionsinvarianten. Reale Dinge weisen komplexe Beziehungsgeflechte auf. Kunden geben Bestellungen auf, Bestellungen enthalten Produkte, Produkte haben Lieferanten usw. Wenn von der Anwendung mehrere zusammengehörige Objekte geändert werden, wie kann dann die Konsistenz gewährleistet werden? Wie können Invarianten nachverfolgt und erzwungen werden?
In herkömmlichen Anwendungen wurden häufig Datenbanktransaktionen eingesetzt, um Konsistenz zu erzwingen. In einer verteilten Anwendung ist dies aber oftmals nicht möglich. Eine einzelne Geschäftstransaktion kann sich unter Umständen über mehrere Datenspeicher erstrecken, eine lange Ausführungsdauer aufweisen oder Drittanbieterdienste umfassen. Letztendlich liegt es an der Anwendung und nicht an der Datenschicht, die für die Domäne erforderlichen Invarianten zu erzwingen. Für die Durchführung dieser Modellierung sind Aggregate bestimmt.
Hinweis
Ein Aggregat kann ggf. aus einer einzelnen Entität ohne untergeordnete Entitäten bestehen. Durch die Transaktionsgrenze wird dies zu einem Aggregat.
Domänen- und Anwendungsdienste: In der DDD-Terminologie ist ein Dienst ein Objekt, mit dem Logik implementiert wird, ohne dass ein Zustand vorgehalten wird. Evans unterscheidet zwischen Domänendiensten, in denen die Domänenlogik gekapselt ist, und Anwendungsdiensten, mit denen die technische Funktionalität bereitgestellt wird, z.B. die Benutzerauthentifizierung oder das Senden einer SMS-Nachricht. Domänendienste werden häufig zum Modellieren von Verhalten verwendet, das mehrere Entitäten umfasst.
Hinweis
Der Begriff Dienst ist im Bereich der Softwareentwicklung mehrfach besetzt. Die hier verwendete Definition bezieht sich nicht direkt auf Microservices.
Domänenereignisse: Domänenereignisse können andere Teile des Systems benachrichtigen, wenn etwas auftritt. Wie der Name schon sagt, sollten Domänenereignisse etwas Sinnvolles innerhalb der Domäne darstellen. "Ein Datensatz wurde in eine Tabelle eingefügt" ist z. B. kein Domänenereignis. "Eine Übermittlung wurde abgebrochen" ist ein Domänenereignis. Domänenereignisse sind in einer Microservices-Architektur besonders wichtig. Da Microservices verteilt sind und keine Datenspeicher freigeben, ermöglichen Domänenereignisse die Koordination zwischen Diensten. Weitere Informationen zu asynchronen Nachrichten finden Sie unter Interservice-Kommunikation.
Es gibt einige andere DDD-Muster, die hier nicht behandelt werden, darunter Fabriken, Repositorys und Module. Diese Muster können hilfreich sein, wenn Sie einen Microservice implementieren, aber sie sind weniger relevant, wenn Sie die Grenzen zwischen Microservices entwerfen.
Drohnenlieferung: Anwenden der Muster
Wir beginnen mit den Szenarien, die vom Kontextgrenzenbereich „Lieferung“ verarbeitet werden müssen.
- Ein Kunde kann eine Drohne anfordern, um Waren von einem Unternehmen abzuholen, das beim Dienst für die Drohnenlieferung registriert ist.
- Der Absender generiert eine Kennzeichnung per Tag (Strichcode oder RFID) für das Paket.
- Eine Drohne holt ein Paket ab und liefert es vom Ausgangsort an den Zielort.
- Wenn ein Kunde eine Lieferung plant, wird vom System eine geschätzte Ankunftszeit angegeben, die auf den Routeninformationen, Wetterbedingungen und Verlaufsdaten basiert.
- Wenn eine Drohne in der Luft ist, kann ein Benutzer den aktuellen Standort und die zuletzt ermittelte geschätzte Ankunftszeit nachverfolgen.
- Der Kunde kann eine Lieferung stornieren, bis eine Drohne das Paket abgeholt hat.
- Der Kunde wird benachrichtigt, wenn die Lieferung abgeschlossen ist.
- Der Absender kann vom Kunden eine Bestätigung der Lieferung in Form einer Signatur oder eines Fingerabdrucks anfordern.
- Benutzer können den Verlauf einer abgeschlossenen Lieferung anzeigen.
Anhand dieser Szenarien hat das Entwicklungsteam die folgenden Entitäten identifiziert.
- Lieferung
- Paket
- Drohne
- Konto
- Bestätigung
- Benachrichtigung
- Etikett
Die ersten vier Entitäten (Lieferung, Paket, Drohne und Konto) sind allesamt Aggregate, die für die Grenzen der Transaktionskonsistenz stehen. Bestätigungen und Benachrichtigungen sind untergeordnete Elemente von Lieferungen, und Tags sind untergeordnete Elemente von Paketen.
Zu den Wertobjekten dieses Entwurfs gehören Standort, geschätzte Ankunftszeit, Paketgewicht und Paketgröße (Location, ETA, PackageWeight und PackageSize).
Zur besseren Veranschaulichung ist hier ein UML-Diagramm des Aggregats für die Lieferung (Delivery) angegeben. Beachten Sie, dass es Verweise auf andere Aggregate enthält, z.B. Konto (Account), Paket (Package) und Drohne (Drone).
Es sind zwei Domänenereignisse vorhanden:
Während eine Drohne in der Luft ist, sendet die Drone-Entität DroneStatus-Ereignisse, mit denen der Standort und Status (Flug, Gelandet) der Drohne beschrieben werden.
Die Delivery-Entität sendet jeweils DeliveryTracking-Ereignisse, wenn sich die Phase einer Lieferung ändert. Die DeliveryTracking-Ereignisse umfassen DeliveryCreated, DeliveryRescheduled, DeliveryHeadedToDropoff und DeliveryCompleted.
Beachten Sie, dass diese Ereignisse Dinge beschreiben, die innerhalb des Domänenmodells eine Bedeutung haben. Sie beschreiben einen Aspekt der Domäne und sind nicht an ein Konstrukt einer bestimmten Programmiersprache gebunden.
Das Entwicklungsteam hat noch einen weiteren Funktionalitätsbereich identifiziert, der nicht ohne Weiteres einer der bisher beschriebenen Entitäten zugeordnet werden kann. Ein Teil des Systems muss alle Schritte koordinieren, die an der Planung oder Aktualisierung einer Lieferung beteiligt sind. Daher hat das Entwicklungsteam dem Entwurf zwei Domänendienste hinzugefügt: einen Scheduler , der die Schritte koordiniert, und einen Supervisor , der den Status der einzelnen Schritte überwacht, um zu erkennen, ob Schritte fehlgeschlagen oder timeout sind. Dieser Ansatz ist eine Variante des Musters "Scheduler Agent Supervisor".
Nächste Schritte
Im nächsten Schritt werden die Grenzen für die einzelnen Microservices definiert.