Mapper-Kontexte Superkontexte: Entkopplung domänenspezifischer und domänengenerischer begrenzter Kontexte

(1. August 2019)

Sie erstellen ein neues System und Zwei Mitglieder Ihres Teams schlagen alternative Architekturen zum Senden von Benachrichtigungen vor. Welches ist richtig?

Der erste Entwickler schlägt ein Push-Modell vor: Begrenzte Kontexte sollten den Benachrichtigungskontext anweisen, eine Benachrichtigung zu senden. Der Benachrichtigungskontext (Benachrichtigungen) sollte einfach Befehlen aus anderen Kontexten folgen und Benachrichtigungen senden, wenn Sie dazu aufgefordert werden.

Der zweite Entwickler mag das Push-Modell nicht und schlägt eine choreografierte Lösung vor: Wenn begrenzte Kontexte Ereignisse auslösen, sollten Benachrichtigungen abhören und bestimmen, wann eine Benachrichtigung gesendet werden soll.

Wie würden Sie die Lösung entwickeln? Noch wichtiger ist, wie würden Sie diese Entscheidung im Team lösen? Wie können Sie die effektivste Architektur entwerfen, die kurzfristige Ziele und langfristige Entwicklungen unterstützt?

DDD-Kenntnisse im Team sind ein großer Vorteil. Wenn Sie Ihre Domain analysieren können, um die wichtigsten, unterstützenden und allgemeinen Funktionen zu verstehen, können Sie fundierte technische Kompromisse eingehen.

Lassen Sie uns dieses Szenario mit einer DDD-Perspektive weiter untersuchen.

Domain Fähigkeitsanalyse

Das Argument für Option 1 (Push-Befehle) ist, dass generische (Benachrichtigungen) nicht von bestimmten abhängig sein sollten. Wenn etwas in vielen Domänen generisch ist, ist es logischerweise falsch, wenn es an etwas in einer einzelnen Domäne gekoppelt ist.

Worüber warnt uns diese heuristische Warnung, wenn wir über das abstrakte Denken hinausblicken? Welche technischen Konsequenzen müssen wir beachten?

Es gibt zwei Szenarien, die wir vermeiden möchten:

  1. Vermeiden, dass domänenspezifische Logik in generische Kontexte gelangt, was zu Zusammenarbeit führt -change
  2. Unfähigkeit, generische Kontexte wiederzuverwenden oder durch eine Standardlösung zu ersetzen, da sie zu eng mit domänenspezifisch begrenzten Kontexten gekoppelt sind

Koppeln von Domänen und generischen Verantwortlichkeiten

Bei Option 1 gibt es keinen Wechsel zwischen domänenspezifischen und domänengenerischen APIs. Wenn eine neue Benachrichtigung benötigt wird, ändern sich nur die domänenspezifischen Kontexte. Dies ist jedoch bei Option 2 nicht der Fall.

Wenn bei Option 2 (choreografiert) ein neues Ereignis eingeführt wird, für das Benachrichtigungen erforderlich sind, muss eine domänenspezifische API das neue Ereignis und den Benachrichtigungsdienst veröffentlichen müssen die Veranstaltung abonnieren und eine Benachrichtigung senden. Dies fühlt sich nicht richtig an – das Wissen über die Domäne versteckt sich im generischen Kontext.

Isolieren generischer Funktionen

Wenn der Benachrichtigungsdienst wirklich generisch ist und in vielen Teams oder sogar in einem wiederverwendet wird Organisation muss es über Hunderte von Domain-Ereignissen wissen. Und bei so vielen Teams, die vom Benachrichtigungskontext abhängen, wird dies sicherlich zu einem Engpass, der das Potenzial für die Wiederverwendung in einem Unternehmen verringert.

Ein weiteres Feedback zum Design ist, dass wir den generischen Kontext nicht durch einen ersetzen können Standardlösung. Wenn es in vielen Bereichen wirklich generisch ist, wir es jedoch nicht durch eine Standardlösung ersetzen können, die mehr Funktionen bietet und weniger Kosten für die Ausführung verursacht, gibt das Design Feedback, dass etwas nicht stimmt.

Befehle von spezifisch zu generisch sind also eine bewährte Methode?

Alle Hinweise deuten darauf hin, dass Option 1 korrekt ist: Domänenspezifische Kontexte sollten Befehle an generische Domänenkontexte senden, um sie von der Domänenlogik zu entkoppeln. Unsere Analyse war jedoch flach. Wir müssen die Domäne weiter analysieren, um das Vertrauen zu gewinnen, dass wir gute technische Entscheidungen treffen.

Tiefere Domänenanalyse

Bei genauerer Betrachtung von Option 1 (spezifische Anweisungen allgemein) für jede Domäne -spezifischer Kontext, der Benachrichtigungen senden muss, hat eine zusätzliche Verantwortung übernommen. Es muss wissen, wann Benachrichtigungen gesendet werden müssen und welche Art von Benachrichtigung gesendet werden soll.

Ist es sinnvoll, die Benachrichtigungslogik über alle begrenzten Kontexte zu verteilen? Zusammen mit verwirrtem Code kann dies eine umfassende Koordination zwischen vielen Teams bedeuten, wenn sich der Benachrichtigungsansatz ändert.

Es besteht auch ein erhöhtes Risiko oder Inkonsistenzen und Doppelarbeit / Verschwendung, wenn jedes Team seinen eigenen Ansatz für Benachrichtigungen verwendet. Gemeinsame Bibliotheken sind eine Möglichkeit, lösen jedoch nicht alle Probleme und bringen auch Kompromisse mit sich.

Einige Domänenkonzepte sind klar unterteilt (rote, graue, gelbe Dreiecke; blaue Kreise), andere jedoch Überlappungen über verschiedene Dimensionen hinweg und können auf verschiedene Arten kategorisiert werden (blaue Dreiecke).

Wenn es schwierig ist, zu entscheiden, ob eine Verantwortung in den einen oder anderen Kontext gehören soll, Zoomen Sie hinein und zerlegen Sie die Verantwortung weiter. Suchen Sie nach Unterverantwortlichkeiten, die getrennt werden können – möglicherweise gibt es ein verstecktes Domänenkonzept.

Wenn ein Konzept nicht sauber in einen einzelnen begrenzten Kontext passt, analysieren Sie es weiter, um Unterverantwortlichkeiten zu identifizieren. Möglicherweise gibt es ein Hidden-Domain-Konzept, das zu einem sauber partitionierten Modell führen kann.

Wenn Sie sich mit der umstrittenen Verantwortung für das Senden von Benachrichtigungen befassen, könnte ein Konzept fehlen ? Möglicherweise gibt es ein drittes Konzept, das domänenspezifisch mit domänengenerisch verknüpft, um ein eleganteres Modell bereitzustellen.

Domain Mapper-Kontexte

Ein Muster, mit dem domänenspezifisch entkoppelt werden kann und domain-generic ist der Domain Mapper-Kontext. Wenn ein Kontext diese Rolle übernimmt, wartet er auf bestimmte Domänenereignisse und ordnet sie Befehlen zu, die an einen generischen Kontext gesendet werden.

Beachten Sie, wie die Kompromisse dieses Musters mit den Vor- und Nachteilen der ersten beiden Optionen verglichen werden. Es bietet die Vorteile von beidem – die Freiheit, die Art und Weise zu ändern, in der Benachrichtigungen gesendet werden, ohne jeden domänenspezifischen Kontext mit Benachrichtigungskomplexität zu überladen.

Stellen Sie sich das Szenario vor: Ein internes Benachrichtigungssystem soll durch ersetzt werden eine Standardlösung. Der Domain-Mapper leitet alle Befehle an den neuen Benachrichtigungsdienst weiter, ohne dass domänenspezifische Kontexte betroffen sind.

Stellen Sie sich ein anderes Szenario vor: Eine neue Benachrichtigung soll hinzugefügt werden. Eine Registrierung wird im Mapper konfiguriert: wenn {Domänenereignis} {Benachrichtigung} auslöst.

Benachrichtigungen sind eine generische Funktion – viele Domains nutzen E-Mail- und Push-Benachrichtigungen
Benachrichtigungseinstellungen auf Twitter – Zuordnung von domänenspezifischem Ereignis zu domänengenerischen Aktionen (E-Mails)

Warum Mapper-Kontexte aufgerufen?

Die Die Benennung dieses Musters ist von Bedeutung. Aktionen, die innerhalb einer Domäne ausgeführt werden, werden Aktionen zugeordnet, die in einer anderen Domäne ausgeführt werden (ein generischer Kontext ist in vielen Domänen generisch, also teilweise in einer anderen Domäne).

Möglicherweise haben Sie Ähnlichkeiten mit anderen Mustern wie der Messaging-Übersetzer , jedoch impliziert die Übersetzung eine gewisse Äquivalenz. Der übersetzte Wert ist eine andere Darstellung des ursprünglichen Werts. Bei einem Mapper ist dies nicht der Fall.

Ein Mapper ist eher ein Listener, der beobachtet, was innerhalb der Domäne geschieht. Es wird entschieden, wie auf Ereignisse in der Domäne mit einer Aktion in einer anderen Domäne reagiert werden soll.

Gateway Domain Mapper-Kontexte

Wenn Sie sich entscheiden, Ihre benutzerdefinierten zu ersetzen Bei einem generischen Kontext mit einer SaaS-Lösung wird Ihr Domain Mapper-Kontext zu einem Gateway Domain Mapper-Kontext.

A. Das Gateway befindet sich am Rand eines Systems, das den Zu- und Abfluss von Informationen verwaltet.

Ein Gateway Domain Mapper-Kontext führt im Wesentlichen dieselbe Funktion aus, befindet sich jedoch jetzt unter der Rand Ihres Systems und kommuniziert über Systemgrenzen hinweg. Dies ist eine Route für den Informationsfluss in und aus dem System.

Die Implementierung sieht zwar gleich aus, aber eine genauere Terminologie ist hilfreich, damit Sie Ihre Architektur klarer kommunizieren können.

Kompromisse beim Domain Mapper Engineering

Die mit Domain Mapper Contexts verbundenen Muster sind nicht unerheblich. Die zusätzliche Zuordnungsschicht bedeutet, dass jetzt drei Mitarbeiter beteiligt sind. Weitere Dinge, die fehlschlagen können.

Es gibt auch Änderungen. Wenn neue Ereignisse eingeführt werden oder vorhandene Ereignisse sich ändern, müssen sich sowohl die domänenspezifischen Kontexte, denen die Ereignisse gehören, als auch der Mapper-Kontext ändern.

Es gibt weit verbreitete Lösungen, um diese Kosten zu minimieren.

Self-Service-Domain-Mapper-Kontexte

Ein in der Natur häufig vorkommendes Muster ist der Self-Service-beschränkte Kontext. Ein begrenzter Kontext, der diese Rolle spielt, ermöglicht es Verbrauchern, die Funktionen des Kontexts zu nutzen, ohne von dem Team blockiert zu werden, dem der Kontext gehört.

Die erste Variante ist ein Kompilierungszeitmechanismus über die Quellcodeverwaltung und die zweite ein Laufzeitmechanismus über API-Aufrufe.

Im Benachrichtigungsszenario kann der Self-Service-Kontext ein DSL bereitstellen. Teams, die domänenspezifische Kontexte besitzen, erstellen eine Pull-Anfrage mit Änderungen in einer Konfigurationsdatei (noch besser – Code, der kompiliert und getestet wird), die mithilfe von DSL eine Zuordnung zwischen einem zu abonnierenden Domänenereignis und einer Benachrichtigung konfiguriert gesendet werden.

Mit der dynamischen Version würde die entsprechende Konfiguration über einen API-Aufruf oder eine Benutzeroberfläche durchgeführt. Wenn eine dynamische Konfiguration möglich ist und keine Abhängigkeiten zur Kompilierungszeit von der Domäne bestehen, verschiebt sich der Kontext in Richtung einer vollständigen Generierung. Dies ist ein allgemeines Evolutionsmuster ().

Veröffentlichte Sprache

Ein weiteres DDD-Muster, das in diesem Szenario berücksichtigt werden muss, ist die veröffentlichte Sprache. Jedes Ereignis, das eine Benachrichtigung auslöst, sollte Teil einer veröffentlichten Sprache sein.

Eine veröffentlichte Sprache ist ein Vertrag oder eine Vereinbarung über das Format von Nachrichten, die in einem begrenzten Kontext erstellt werden. Um sicherzustellen, dass Ereignisse, für die eine Benachrichtigung erforderlich ist, Teil einer veröffentlichten Sprache sind, sollte größere Sorgfalt darauf verwendet werden, die Abwärtskompatibilität sicherzustellen und die Verbraucher über zukünftige Änderungen zu informieren.

Soll ich immer Domain Mapper-Kontexte verwenden?

Auf keinen Fall. Jedes der im Beitrag vorgestellten Muster ist gültig und wird in einer Vielzahl von Systemen erfolgreich verwendet. Domain Mapper-Kontexte sind ein weiteres Muster mit klaren Kompromissen, mit denen Sie alternative Modellierungsmöglichkeiten untersuchen können.

Die Suche nach Superkontexten

Es ist einfach, Domains auf einer naiven oder oberflächlichen Ebene zu modellieren . Wenn Sie ein Wort wie „Benachrichtigungen“ hören, fällt es leicht, auf den zusammenhängenden Namensfehler hereinzufallen, vorausgesetzt, ein Wort klingt wie ein einzelnes Konzept und muss in unserem System durch einen einzelnen begrenzten Kontext dargestellt werden.

Wenn wir DDD verwenden, um auf einer tieferen Ebene zu analysieren, und wir versuchen, domänenspezifische und domänengenerische Konzepte zu trennen, werden wir häufig feststellen, dass es mehrere Möglichkeiten gibt, die Domäne zu modellieren, einschließlich mehrerer begrenzter Kontexte – Abgrenzung domänenspezifisch von generisch – innerhalb eines einzigen Superkontexts.

Bei der Entkopplung domänenspezifisch von domänengenerisch geht es nicht darum, schöne abstrakte Modelle zu erstellen, die alten DDD-Regeln folgen, sondern darum, Konzepte zu entkoppeln, die sich aus verschiedenen Gründen gemeinsam ändern, damit wir den Handel entwickeln können -offs, mit denen sich Systeme leichter weiterentwickeln können.

Versuchen Sie, den Irrtum der Klassifizierung einzelner Domänen zu vermeiden: Nehmen Sie an, dass eine übergeordnete Funktion, Benachrichtigungen, eine Klassifizierung einzelner Domänen (z. B. generisch) haben muss.

In orde Um tiefere Einblicke in die Domäne zu erhalten, empfehle ich, DDD-Fähigkeiten und DDD-Aktivitäten in die Arbeitsweise Ihres Teams einzubeziehen. Experimentieren Sie mit EventStorming und (The Bounded Context Design Canvas) als Ausgangspunkt, um zu lernen, wie Sie besser begrenzte Kontexte modellieren.

Schulung und Beratung

Wenn Sie Hilfe beim Erkunden suchen Ihre Domain, das Entwerfen Ihres Systems oder das Trainieren Ihrer Teams. Kontaktieren Sie mich für weitere Informationen. Ich arbeite mit einem Netzwerk von erfahrenen DDD-Praktikern zusammen, die leidenschaftlich gerne effektive soziotechnische Systeme entwickeln, die auf die Geschäftsziele und den Geschäftsbereich abgestimmt sind.