Domain-gesteuertes Design: ein Rezept fĂŒr einen Pragmatiker


Warum werden DDDs normalerweise von der falschen Seite angegangen? Und welche Seite willst du? Was haben Giraffen und Schnabeltiere damit zu tun?

Speziell fĂŒr Habr - eine Textabschrift des Berichts "Domain-gesteuertes Design: ein Rezept fĂŒr einen Pragmatiker". Der Bericht wurde auf der DotNext .NET-Konferenz erstellt, kann jedoch nicht nur fĂŒr Spender, sondern fĂŒr alle, die an DDD interessiert sind, nĂŒtzlich sein (wir glauben, dass Sie einige C # -Codebeispiele beherrschen werden). Eine Videoaufzeichnung des Berichts ist ebenfalls beigefĂŒgt.



Hallo allerseits, mein Name ist Alexey Merson. Ich werde Ihnen sagen, was Domain-Driven Design ist und was es ausmacht, aber zuerst wollen wir herausfinden, warum es ĂŒberhaupt benötigt wird.



Martin Fowler sagte: "Es gibt wenige Dinge, die weniger logisch sind als GeschĂ€ftslogik." Die Giraffe ist definitiv eine dieser wenigen. Der Abstand zwischen Gehirn und Kehlkopf einer Giraffe betrĂ€gt nur wenige Zentimeter. Der Nerv, der sie verbindet, erreicht jedoch 4 Meter. Zuerst geht er durch den ganzen Hals, dort geht er um die Arterie herum und dann geht er fast auf die gleiche Weise zurĂŒck.

Auf den ersten Blick gibt es wirklich keine Logik. Aber dies ist nur ein dichtes Erbe, das von alten Fischen ĂŒbrig geblieben ist. Wie Sie wissen, gibt es bei Fischen keinen Hals, sodass dieser Nerv auf dem optimalen Weg verlĂ€uft. Und als SĂ€ugetiere nach mehreren Millionen Jahren Refactoring auftauchten, musste der Nerv verlĂ€ngert werden, um die AbwĂ€rtskompatibilitĂ€t aufrechtzuerhalten. Nun, nicht wegen einer Giraffe umbauen?

Aber die Giraffe ist okay, weil es ein Schnabeltier gibt.


Überleg es dir. Das SĂ€ugetier Mit einem Schnabel. Lebt hauptsĂ€chlich im Wasser. Legt Eier. Und außerdem giftig. Es scheint, dass die einzig logische ErklĂ€rung fĂŒr seine Existenz darin besteht, dass er aus Australien stammt.

Aber ich denke, dass alles banaler ist. Der Auftragnehmer vergaß einfach das Design und sparte mit StackOverflow, na ja, oder was damals da war.

Ich weiß, was Sie jetzt denken: "Alexey, Sie haben uns Domain-Driven Design versprochen, und hier ist eine Art" In der Tierwelt "!"

Kollegen, was ist Entwicklung? Entwicklung ist, wenn wir einen Teil der realen Welt, einen GeschÀftsprozess, in Code umwandeln, dh ein Softwaremodell erstellen. Welche Probleme erwarten uns auf dem Weg?

Das erste ist die KomplexitÀt der GeschÀftsprozesse selbst, dh die Schwierigkeit zu verstehen, wie das GeschÀft funktioniert, welche Prozesse dort stattfinden und nach welcher Logik sie aufgebaut sind.

Das zweite Problem ist die Implementierung dieser GeschÀftsprozesse in Form von Code, die Verwendung der richtigen Muster, der richtigen AnsÀtze usw. Dies ist auch ein ziemlich kompliziertes Thema.

Schauen Sie, GeschĂ€ftsprozesse sind wie diese Giraffe: Sie haben mit den einfachsten einzelligen begonnen, und dann sieht „das“ Sie an, und niemand versteht entweder „woher es kommt“ oder „wie es funktioniert“.

Um ein erfolgreiches Modell eines solchen Prozesses zu erstellen, mĂŒssen Sie zuerst die Frage „Warum?“ Beantworten. Warum wollen wir dieses Modell bauen? Welche Ziele wollen wir erreichen? Wenn der Kunde eine ausgestopfte Giraffe haben möchte, aber den Mut dazu hat, ist er schließlich verĂ€rgert, auch wenn die Verdauung in diesem Modell fĂŒr eine Augenweide durchgefĂŒhrt wird. Und der Kunde wird nicht nur Geld und Zeit verlieren, er wird auch das Vertrauen in uns als Entwickler verlieren und wir werden unseren Ruf und Kunden verlieren.

Aber selbst wenn wir die Ziele herausgefunden haben, garantiert dies nicht, dass wir das Schnabeltier als Ergebnis nicht bekommen. Tatsache ist, dass das Ziel wenig zu verstehen ist. Das Ziel muss erreicht werden. Und das hilft uns beim domÀnengesteuerten Design.

Das Hauptziel von Domain-Driven Design ist es, die KomplexitÀt von GeschÀftsprozessen sowie deren Automatisierung und Implementierung in Code zu bekÀmpfen. "Domain" bedeutet "Domain", und Entwicklung und Design im Rahmen dieses Ansatzes werden von der Domain verdrÀngt.

Domain-Driven Design beinhaltet viele Dinge. Dieses strategische Design und die Interaktion zwischen Menschen, ArchitekturansĂ€tzen und taktischen Mustern - dies ist ein ganzes Arsenal, das wirklich funktioniert und wirklich hilft, Projekte zu machen. Es gibt nur ein "aber". Bevor Sie sich mit der KomplexitĂ€t von Domain-Driven Design befassen, mĂŒssen Sie lernen, wie Sie mit der KomplexitĂ€t von Domain-Driven Design selbst umgehen.



Wenn eine Person beginnt, sich mit diesem Thema zu beschĂ€ftigen, fĂ€llt eine große Menge an Informationen auf sie: dicke BĂŒcher, eine Reihe von Artikeln, Mustern, Beispielen. All dies ist verwirrend und es ist leicht, wie sie sagen, hinter den BĂ€umen des Waldes nicht zu bemerken. Ich habe das einmal an mir selbst gespĂŒrt, aber heute möchte ich meine Erfahrungen mit Ihnen teilen und Ihnen helfen, durch diesen Dschungel zu kommen und endlich Domain-Driven Design zu verwenden.

Der Begriff Domain-Driven Design selbst wurde 2003 von Eric Evans in seinem unaussprechlichen Buch vorgeschlagen, das die Community einfach als Blue Book bezeichnet. Das Problem ist, dass in der ersten HĂ€lfte des Buches Evans ĂŒber taktische Muster gesprochen wird (Sie alle kennen sie: Dies sind Fabriken, Unternehmen, Endlager, Dienstleistungen) und die Leute normalerweise nicht in die zweite HĂ€lfte gelangen. Der Mann sieht aus: Alles ist bekannt, ich hole die DDD-Anwendung.

Auf der rechten Seite passiert, wenn Sie wahnsinnig taktische Muster auf den Compiler werfen. Links - wenn Sie strategische Muster verwenden.



Seit der Veröffentlichung des Blue Book hat sich eine ziemlich starke DDD-Community gebildet, viele Dinge wurden ĂŒberdacht. Ja, und Evans selbst gab zu, dass er nicht mehr versteht, wie er einer so wichtigen Sache wie dem strategischen Design ein Ende setzen kann.

Und 10 Jahre spĂ€ter, 2013, wurde das Rote Buch von Vaughn Vernon veröffentlicht. Und in diesem Buch ist die PrĂ€sentation bereits in der richtigen Reihenfolge aufgebaut: Sie beginnt mit dem strategischen Design, mit den Grundlagen. Und wenn der Leser die notwendige Basis erhalten hat, beginnt er bereits ĂŒber taktische Muster und Implementierungsdetails zu sprechen.

Normalerweise empfehlen sie in DDD-Berichten das Lesen von Evans. Im Internet gibt es sogar ganze HandbĂŒcher, in deren Reihenfolge Sie die Kapitel lesen mĂŒssen, um richtig eintauchen zu können. Ich empfehle, es einfacher zu machen: Beginnen Sie mit dem Roten Buch, lesen Sie es und fahren Sie erst dann mit Blau fort.

Und da strategisches Design so wichtig ist, lassen Sie uns ĂŒber seine SchlĂŒsselideen sprechen.

"SchlĂŒsselideen des strategischen Designs"


In jedem GeschĂ€ftsautomatisierungsprojekt gibt es immer Domain-Experten. Dies sind Menschen, die am besten verstehen, wie die zu modellierenden GeschĂ€ftsprozesse funktionieren. Dies können fĂŒhrende Entwickler, FĂŒhrungskrĂ€fte und Top-Manager sein. Im Allgemeinen kann es jeder sein, wenn er nur die GeschĂ€ftsprozesse versteht, mit denen wir uns befassen mĂŒssen.



Auf der anderen Seite gibt es technische Experten: Entwickler, Architekten, die direkt an der Automatisierung und Implementierung von Anwendungen beteiligt sind. In dem abgebildeten Beispiel wollte der Kunde wahrscheinlich eine Kinderbahn, aber es stellte sich heraus, dass es sich um eine Art Monster handelte.

Warum passiert das? Weil die Interaktion zwischen technischen Experten und Domain-Experten in einer typischen Situation ungefĂ€hr so ​​aussieht: Es gibt eine große Wand zwischen ihnen, und ein Manager geht oben an dieser Wand entlang und versucht zuerst zu hören, was sie auf einer Seite der Wand schreien, dann versucht er, es den besten BĂŒndeln zu schreien auf der anderen Seite der Wand und so weiter im Kreis.

Manchmal ist ein Manager taub, dann kann eine ganze Kette solcher Manager aufgebaut werden, was natĂŒrlich nicht zum Erfolg des Projekts beitrĂ€gt. Und wie soll es sein?



Es muss eine stĂ€ndige Interaktion geben. Technische Experten, Domain-Experten - Alle Projektteilnehmer mĂŒssen stĂ€ndig kommunizieren, synchronisieren, Ziele diskutieren, Wege finden, um sie zu erreichen, und warum wir das alles tun.

Und hier kommen wir zum ersten und wahrscheinlich wichtigsten Punkt sowohl des strategischen Designs als auch des domÀnengesteuerten Designs im Allgemeinen.



Die Kommunikation zwischen den Projektteilnehmern bildet das, was Domain-Driven Design als allgegenwĂ€rtige Sprache bezeichnet. Er ist nicht einer in dem Sinne, dass er einer fĂŒr alle Gelegenheiten ist. Im Gegenteil. Es ist einfach in dem Sinne, dass alle Teilnehmer darin kommunizieren, alle Diskussionen in einer einzigen Sprache stattfinden und alle Artefakte maximal in einer einzigen Sprache sein sollten, dh beginnend mit TK und endend mit einem Code.

GeschÀftsszenarien


FĂŒr die weitere Diskussion benötigen wir eine Art GeschĂ€ftsszenario. Stellen wir uns diese Situation vor:



Der Direktor der JUG.ru-Gruppe kommt zu uns und sagt: "Leute, der Fluss der Berichte wÀchst, die Menschen werden im Allgemeinen gefoltert, um alles manuell zu erledigen ... Lassen Sie uns den Prozess der Vorbereitung der Konferenz automatisieren." Wir antworten: "Okay!" - und mach dich an die Arbeit.

Das erste Szenario, das wir automatisieren werden, lautet: „Der Redner reicht bei einem bestimmten Ereignis einen Antrag fĂŒr einen Bericht ein und fĂŒgt Informationen zu seinem Bericht hinzu.“ Was sehen wir in diesem Szenario? Was ist ein Sprecher, es gibt ein Ereignis und es gibt einen Bericht, was bedeutet, dass es bereits möglich ist, das erste DomĂ€nenmodell zu erstellen.



Hier haben wir ein Domain-Modell: Sprecher - Sprecher, Talk - Bericht, Event - Event. Das DomÀnenmodell kann jedoch nicht unbegrenzt sein, kann nicht alles abdecken, da es sonst verschwommen wird und den Fokus verliert. Daher muss das DomÀnenmodell durch etwas eingeschrÀnkt werden. Dies ist der nÀchste wichtige Punkt.



Sowohl das DomÀnenmodell als auch die allgegenwÀrtige Sprache sind durch den Kontext begrenzt, den Domain-Driven Design als begrenzten Kontext bezeichnet. Er schrÀnkt das DomÀnenmodell so ein, dass alle darin enthaltenen Konzepte eindeutig sind und jeder versteht, worum es geht.

Wenn sie "Benutzer" sagen, sollte alles auf einmal klar sein, es sollte eine verstÀndliche Rolle haben, eine verstÀndliche Bedeutung, es sollte aus Sicht der IT-Branche keine Art abstrakter Benutzer sein.



In unserem Fall gilt dieses DomĂ€nenmodell fĂŒr den Kontext der Vorbereitung der Konferenz. In diesem Kontext werden wir den Kontext „Ereignisplanung“ nennen. Aber damit der Sprecher etwas hinzufĂŒgen und Informationen Ă€ndern kann, muss er sich irgendwie anmelden und ihm mĂŒssen einige Rechte eingerĂ€umt werden. Und dies wird bereits ein anderer Kontext sein, der „IdentitĂ€tskontext“, in dem es eine Art eigener EntitĂ€ten geben wird: Benutzer, Rolle, Profil.

Und schau was das Ding hier ist. Wenn sich eine Person beim System anmeldet und beabsichtigt, Informationen einzugeben, ist dies physisch dieselbe Person, aber in unterschiedlichen Kontexten wird sie von verschiedenen EntitÀten dargestellt, und diese EntitÀten sind nicht direkt miteinander verbunden.

Wenn wir beispielsweise Speaker von User ĂŒbernehmen und erben wĂŒrden, wĂŒrden wir Dinge mischen, die nicht gemischt werden können, und einige Attribute könnten durch Logik gemischt werden. Und das Modell wĂŒrde den Fokus auf die spezifische Bedeutung verlieren, die es hat, und in mehrere Kontexte unterteilt werden.

Demo: Verkaufsservice


Lassen Sie uns ein wenig von der Trockentheorie abweichen und den Code betrachten.

Eine Konferenz ist nicht nur die Vorbereitung von Inhalten, sondern auch der Verkauf. Stellen wir uns vor, ein Service fĂŒr den Verkauf von Tickets wurde bereits geschrieben, und ein Verkaufsleiter kommt zu uns und sagt: „Leute! Wenn jemand diesen Service geschrieben hat, ist mir nicht klar, wie der Rabatt fĂŒr Stammkunden berĂŒcksichtigt wird. “

Nach einem GesprĂ€ch mit dem Manager stellen wir fest, dass das gesamte Szenario dieses Service das folgende ist: Wenn Sie auf Kasse klicken, wird der endgĂŒltige Ticketpreis unter BerĂŒcksichtigung des regulĂ€ren Kundenrabattes berĂŒcksichtigt, und die Bestellung wird in den Status "Warten auf Zahlung" versetzt.

Der Code, den wir jetzt analysieren, kann separat im Repository angezeigt werden.

Öffnen Sie die Lösung und sehen Sie sich die Struktur an:



Es sieht so aus, als ob alles gut aussieht: Es gibt Anwendung und Kern (anscheinend wissen die Leute ĂŒber Ebenen Bescheid), Repository ... Anscheinend hat die Person die erste HĂ€lfte von Evans gemeistert.

Öffnen Sie OrderCheckoutService. Was sehen wir dort? Hier ist der Code :

public void Checkout(long id) { var ord = _ordersRepository.GetOrder(id); var orders = _ordersRepository.GetOrders() .Count(o => o.CustomerId == ord.CustomerId && o.StateId == 3 && o.OrderDate >= DateTime.UtcNow.AddYears(-3)); ord.Price *= (100 - (orders >= 5 ? 30m : orders >= 3 ? 20m : orders >= 1 ? 10m : 0)) / 100; ord.StateId = 1; _ordersRepository.SaveOrder(ord); } 


Wir betrachten die Linie mit Preis: hier Ă€ndert sich der Preis. Wir rufen unseren Verkaufsleiter an und sagen: „Hier, kurz gesagt, wird hier der Rabatt berĂŒcksichtigt, alles ist klar“:

 ord.Price *= (100 - (orders >= 5 ? 30m : orders >= 3 ? 20m : orders >= 1 ? 10m : 0)) / 100; 


Er schaut ĂŒber die Schulter: „Oh! So sieht also Brainfuck aus! Und sie sagten mir irgendwie, dass die Jungs in C # schreiben. “

Offensichtlich hat der Entwickler dieses Codes gut auf ein Interview ĂŒber Algorithmen und Datenstrukturen reagiert. Ich schrieb in der Schule Olympiaden in etwa dem gleichen Stil. Nach einiger Zeit finden wir mithilfe von Formatierung und obszönem Refactoring heraus, was was ist, und erklĂ€ren unserem langmĂŒtigen Vertriebsleiter, dass die Logik folgende lautet: Wenn die Anzahl der Bestellungen in den letzten 3 Jahren nicht weniger als eins betrĂ€gt, erhĂ€lt er 10% Rabatt nicht weniger als drei - 20% und nicht weniger als fĂŒnf - 30%. Er geht freudig - jetzt ist klar, wie das alles funktioniert.

Ich denke, viele haben Bob Martins Clean Code gelesen. Dort sagt er ĂŒber die Pfadfinderregel: "Der Parkplatz nach unserer Abreise sollte sauberer sein als vor unserer Ankunft." Lassen Sie uns diesen Code daher so umgestalten, dass er menschlich aussieht und dem entspricht, worĂŒber wir etwas frĂŒher ĂŒber die allgegenwĂ€rtige Sprache und ihre Verwendung im Code gesprochen haben.

Hier ist der ĂŒberarbeitete Code.

  public class DiscountCalculator { private readonly IOrdersRepository _ordersRepository; public DiscountCalculator(IOrdersRepository ordersRepository) { _ordersRepository = ordersRepository; } public decimal CalculateDiscountBy(long customerId) { var completedOrdersCount = _ordersRepository.GetLast3YearsCompletedOrdersCountFor(customerId); return DiscountBy(completedOrdersCount); } private decimal DiscountBy(int completedOrdersCount) { if (completedOrdersCount >= 5) return 30; if (completedOrdersCount >= 3) return 20; if (completedOrdersCount >= 1) return 10; return 0; } } 


Als erstes ĂŒbertragen wir die Rabattberechnung auf einen separaten DiscountCalculator, in dem die CalculateDiscountBy customerId-Methode angezeigt wird. Alles wird menschlich gelesen, alles ist klar: was, warum und wie. Innerhalb dieser Methode sehen wir, dass wir global zwei Schritte haben, um den Rabatt zu berechnen. Erstens: Wir erhalten etwas aus dem Auftrags-Repository. Alles hĂ€ngt vom Benutzerfall ab. Sie mĂŒssen nicht einmal hineingehen, wenn dies nicht der Teil ist, der Sie jetzt interessiert. Tatsache ist, dass wir die Anzahl einiger abgeschlossener Bestellungen erhalten, wonach wir den zweiten Rabatt fĂŒr diese Menge sofort als zweiten Schritt betrachten.

Wenn wir sehen wollen, wie es betrachtet wird, gehen wir zu DiscountBy, und hier ist fast das gleiche in fast menschlichem Englisch geschrieben, das unsere "Art von Brainfuck" vorher war, alles ist klar und prÀzise.

Die einzige Frage, die sich stellen könnte, ist, in welchen Einheiten der Rabatt gemessen wird. Es wĂ€re möglich, das Wort „Prozent“ in den Namen der Methode einzufĂŒgen, um dies zu verdeutlichen. Aus dem Kontext und den Zahlen geht jedoch höchstwahrscheinlich hervor, dass es sich um ProzentsĂ€tze handelt, und der KĂŒrze halber kann es weggelassen werden. Wenn wir sehen möchten, wie viele Bestellungen es gab, gehen wir zum Repository-Code und sehen. Jetzt werden wir das nicht tun. In unserem Service mĂŒssen wir eine neue DiscountCalculator-AbhĂ€ngigkeit hinzufĂŒgen. Und mal sehen, was wir in der zweiten Version der Checkout-Methode erreicht haben.

 public void CheckoutV2(long orderId) { var order = _ordersRepository.GetOrder(orderId); var discount = _discountCalculator.CalculateDiscountBy(order.CustomerId); order.ApplyDiscount(discount); order.State = OrderState.AwaitingPayment; _ordersRepository.SaveOrder(order); } 


Schauen Sie, die Checkout-Methode empfĂ€ngt die Bestell-ID und erhĂ€lt dann eine Bestell-ID fĂŒr die Bestell-ID. Entsprechend der Kunden-ID dieser Bestellung berĂŒcksichtigt sie den Rabatt mithilfe des Rabattrechners, wendet den Rabatt auf die Bestellung an, setzt den Status auf Warten auf Zahlung und speichert die Bestellung. Wir hatten ein Skript in russischer Sprache auf der Folie, aber hier lesen wir praktisch die Übersetzung dieses Skripts ins Englische und alles ist klar, alles ist offensichtlich.

Sehen Sie, was der Charme ist? Dieser Code kann jedem angezeigt werden: nicht nur Programmierern, sondern auch QualitĂ€tssicherern, Analysten und Kunden. Sie werden alle verstehen, was passiert, weil alles in menschlicher Sprache geschrieben ist. Ich benutze dies in unserem Projekt. Die QualitĂ€tssicherung kann sich wirklich einige Teile ansehen, im Wiki nachsehen und verstehen, dass es eine Art Fehler gibt. Weil das Wiki das sagt und der Code etwas anders ist, aber er versteht, was dort passiert, obwohl er kein Entwickler ist. Auf die gleiche Weise können wir den Code mit dem Analysten besprechen und ihn ausfĂŒhrlich besprechen. Ich sage: "Sehen Sie, so funktioniert es im Code." Unser letzter Ausweg ist nicht das Wiki, sondern der Code. Alles funktioniert so, wie es im Code geschrieben steht. Es ist sehr wichtig, beim Schreiben von Code eine allgegenwĂ€rtige Sprache zu verwenden.



Dies ist der dritte wichtige Punkt.

Es gibt so viel Verwirrung ĂŒber Domain-Driven Design in Dingen wie Domain, Subdomain, Bounded-Kontext, wie sie sich auf das beziehen, was sie bedeuten. Es scheint, dass jeder etwas einschrĂ€nkt, alle sind irgendwie aufgerĂ€umt. Aber es ist dann nicht klar, was der Unterschied ist, warum sie so unterschiedlich erfunden sind.



Domain ist eine globale Sache, es ist ein globaler Themenbereich, in dem dieses spezielle GeschĂ€ft Geld verdient. FĂŒr DotNext ist dies beispielsweise eine Konferenz, fĂŒr Pyaterochka ein Einzelhandelsverkauf von Waren.

Große Unternehmen können mehrere DomĂ€nen haben. Beispielsweise befasst sich Amazon sowohl mit dem Verkauf von Waren ĂŒber das Internet als auch mit der Bereitstellung von Cloud-Diensten. Dies sind verschiedene Themenbereiche.

Trotzdem ist es etwas Globales und kann nicht direkt automatisiert werden, selbst wenn es schwierig ist, es zu untersuchen. Zur Analyse wird die DomÀne unweigerlich in SubdomÀnen unterteilt, dh in SubdomÀnen.



Subdomains sind Teile eines Unternehmens, die in unserer Sprache eng miteinander verbunden sind, dh isolierte logische Prozesse, die auf einer wichtigen Ebene miteinander interagieren.

Wenn wir zum Beispiel einen Online-Shop nutzen, wird dies die Bildung und Bearbeitung von Bestellungen sein, es wird die Lieferung sein, dies ist die Arbeit mit Lieferanten, dies ist Marketing, dies ist Buchhaltung. Hier sind einige dieser Teile - in diese Bereiche ist das GeschÀft unterteilt.



Aus Sicht von DDD werden Subdomains in drei Typen unterteilt. Und hier möchte ich noch eines sagen: Oft wird die Subdomain in BĂŒchern und Artikeln einfach auf Domain reduziert, normalerweise jedoch, wenn sie mit dem Subdomain-Typ kombiniert wird. Das heißt, wenn sie "Core Domain" sagen, meinen sie Core Subdomain, bitte verwechseln Sie dies nicht. Es hat mich zuerst umgehauen.

Subdomains werden in drei Typen unterteilt.



Der erste und wichtigste ist Core. Kern ist die wichtigste Subdomain, dies ist der Wettbewerbsvorteil des Unternehmens, was dieses Unternehmen Geld verdient, wie es sich von seinen Mitbewerbern unterscheidet, sein Know-how, wie auch immer Sie es nennen. Wenn wir an der DotNext-Konferenz teilnehmen, ist dies der Inhalt. Sie alle sind wegen Inhalten hierher gekommen. Wenn es hier keine solchen Inhalte gĂ€be, wĂŒrden Sie nicht zu einer anderen Konferenz gehen oder gehen. Es wĂŒrde kein DotNext in der Form geben, in der es ist.



Der zweite Typ ist Supporting Subdomain. Dies ist auch eine wichtige Sache, um Geld zu verdienen, es ist auch etwas, ohne das es unmöglich ist, aber es ist keine Art von Know-how, ein echter Wettbewerbsvorteil. Dies wird von der Core Subdomain unterstĂŒtzt. Unter dem Gesichtspunkt der Anwendung von Domain-Driven Design bedeutet dies, dass weniger Aufwand fĂŒr die UnterstĂŒtzung von Subdomain aufgewendet wird und alle HauptkrĂ€fte auf Core geworfen werden.

Ein Beispiel fĂŒr denselben DotNext ist das Marketing. Ohne Marketing ist das nicht möglich, sonst hĂ€tte niemand etwas ĂŒber die Konferenz gewusst, aber ohne Content-Marketing ist kein Marketing erforderlich.



Und schließlich die generische Subdomain. Generisch ist eine typische GeschĂ€ftsaufgabe, die in der Regel mit fertigen Produkten automatisiert oder ausgelagert werden kann. Dies ist auch erforderlich, erfordert jedoch nicht unbedingt eine unabhĂ€ngige Implementierung durch uns, und darĂŒber hinaus ist es normalerweise eine gute Idee, ein Produkt eines Drittanbieters zu verwenden.

Zum Beispiel Tickets verkaufen. DotNext verkauft Tickets ĂŒber TimePad. Diese Subdomain wird von TimePad perfekt automatisiert, und Sie mĂŒssen kein zweites TimePad selbst schreiben.



Und schließlich ein begrenzter Kontext. Begrenzter Kontext und Subdomain befinden sich immer irgendwo in der NĂ€he, aber es gibt einen signifikanten Unterschied zwischen ihnen. Es ist sehr wichtig.



Bei StackExchange gibt es eine Frage, wie sich der begrenzte Kontext von der Subdomain unterscheidet. Subdomain ist ein StĂŒck GeschĂ€ft, ein StĂŒck der realen Welt, es ist das Konzept eines Problem Statement Space. Der begrenzte Kontext begrenzt das DomĂ€nenmodell und die allgegenwĂ€rtige Sprache, dh das Ergebnis der Modellierung, und dementsprechend ist der begrenzte Kontext das Konzept eines Lösungsraums. WĂ€hrend der Projektimplementierung findet eine Art Mapping von Subdomains in begrenzten Kontexten statt.



Ein klassisches Beispiel: Die Buchhaltung als Subdomain, wie der Prozess abgebildet wird, wird automatisiert, z. B. 1C Bookkeeping, Elba oder „My Business“ - wird von einem Produkt irgendwie automatisiert. Dies ist der begrenzte Kontext des Rechnungswesens, in dem es seine allgegenwĂ€rtige Sprache, seine eigene Terminologie gibt. Das ist der Unterschied zwischen ihnen.



Wenn wir zu DotNext zurĂŒckkehren, werden, wie gesagt, Tickets TimePad zugeordnet, und Inhalte, die unsere Kern-Subdomain sind, werden einer benutzerdefinierten Anwendung zugeordnet, die wir fĂŒr die Inhaltsverwaltung entwickeln.

Begrenzte KontextgrĂ¶ĂŸe


Es gibt einen Moment, der viele Fragen aufwirft. Wie wĂ€hle ich die richtige GrĂ¶ĂŸe fĂŒr den begrenzten Kontext? In BĂŒchern findet man eine solche Definition: "Der begrenzte Kontext muss genau so sein, dass die allgegenwĂ€rtige Sprache vollstĂ€ndig, konsistent, eindeutig, eindeutig, konsistent ist." Coole Definition im Stil eines Mathematikers aus einem berĂŒhmten Witz: sehr genau, aber nutzlos.

Lassen Sie uns diskutieren, wie wir es trotzdem verstehen: ob es sich um eine Lösung, ein Projekt oder einen Namespace handeln soll - welche Skala sollte an den begrenzten Kontext angehÀngt werden?



Das erste, was Sie fast ĂŒberall lesen können: Idealerweise sollte eine Subdomain einem begrenzten Kontext zugeordnet werden , dh durch einen begrenzten Kontext automatisiert werden. Es klingt logisch, da sowohl dort als auch dort EinschrĂ€nkungen eines separaten GeschĂ€ftsprozesses bestehen. In beiden FĂ€llen wird in einigen GeschĂ€ftsbegriffen eine einzige Sprache angezeigt. Aber hier mĂŒssen Sie verstehen, dass dies eine ideale Situation ist, Sie werden dies nicht unbedingt haben, und es ist nicht notwendig, zu versuchen, dies zu erreichen.

Da die Subdomain einerseits sehr groß sein kann und mehrere Anwendungen oder Dienste abgerufen werden können, die sie automatisieren, kann sich herausstellen, dass mehrere begrenzte Kontexte einer Subdomain entsprechen.

Aber es gibt eine umgekehrte Situation, in der Regel ist dies typisch fĂŒr Legacy. Das heißt, wenn sie eine große, große Anwendung erstellt haben, die in diesem Unternehmen alles auf der Welt automatisiert, wird sich das Gegenteil herausstellen. Eine Anwendung ist ein begrenzter Kontext. Dort wird das Modell wahrscheinlich mehrdeutig sein, aber Subdomains sind nicht aus diesem verschwunden. Ein begrenzter Kontext entspricht mehreren Subdomains.

Als die Microservice-Architektur in Mode kam, erschien eine weitere Empfehlung (obwohl sie sich nicht widersprechen): ein begrenzter Kontext pro Microservice . Wieder klingt es logisch, die Leute machen das wirklich. Weil der Mikrodienst eine klare Funktion ĂŒbernehmen muss, die intern eine hohe KonnektivitĂ€t aufweist und ĂŒber eine Art Interaktion mit anderen Diensten kommuniziert. Wenn Sie eine Microservice-Architektur verwenden, können Sie diese Empfehlung selbst ĂŒbernehmen.

Das ist aber noch nicht alles. Ich möchte Sie noch einmal daran erinnern, dass es bei Domain-Driven Design um viel geht: um die Sprache, um Menschen. Und Sie können Personen nicht ignorieren und in dieser Angelegenheit nur technische Kriterien erfĂŒllen. Deshalb habe ich folgendes geschrieben: Ein Kontext ist gleich X-Man . FrĂŒher dachte ich, dass x ungefĂ€hr 10 ist, aber wir haben ein bisschen mit Igor Labutin ( twitter.com/ilabutin ) gesprochen und die Frage blieb offen.

Hier ist es wichtig, dies zu verstehen: Eine einzige Sprache bleibt einheitlich, wĂ€hrend alle Teilnehmer sprechen, diskutieren und jeder sie eindeutig versteht. Es ist klar, dass unendlich viele Menschen nicht dieselbe Sprache sprechen können. Unsere Geschichte der Menschheit zeigt dies deutlich. In jedem Fall erscheinen einige Dialekte, einige ihrer Bedeutungen, jetzt können Sie sogar Meme hinzufĂŒgen und so weiter. Auf die eine oder andere Weise wird die Sprache verschwimmen.

Daher muss verstanden werden, dass die Anzahl der Personen, die diese einzelne Sprache verwenden und dementsprechend an der Entwicklung und Automatisierung teilnehmen, begrenzt ist. In den BĂŒchern wird auch ĂŒber einige politische GrĂŒnde gesprochen: Wenn zwei Teams unter der FĂŒhrung verschiedener Manager arbeiten und an demselben begrenzten Kontext arbeiten und diese Manager aus irgendeinem Grund nicht miteinander befreundet sind, beginnen Konflikte und der Fokus geht verloren. Daher ist es viel einfacher und korrekter, fĂŒr jeden Befehl zwei begrenzte Kontexte zu erstellen und nicht zu versuchen, das zu kombinieren, was nicht kombiniert wird.

Architektur und AbhÀngigkeitsmanagement


Aus Sicht von Domain-Driven Design spielt es keine Rolle, fĂŒr welche Architektur Sie sich entscheiden. Bei Domain-Driven Design geht es nicht darum, bei Domain-Driven Design geht es um Sprache und Kommunikation.



Unter dem Gesichtspunkt der Kriterien fĂŒr die Auswahl der Architektur, die uns aus Sicht des domĂ€nengesteuerten Designs interessiert, gibt es jedoch einen wichtigen Punkt: Unser Ziel ist es, die GeschĂ€ftslogik von AbhĂ€ngigkeiten Dritter maximal zu befreien . Denn sobald AbhĂ€ngigkeiten von Drittanbietern auftreten, wird die Terminologie angezeigt, und es werden Wörter angezeigt, die nicht in eine einzige Sprache eingehen und unsere GeschĂ€ftslogik verunreinigen.



Schauen wir uns ein klassisches Beispiel fĂŒr Architektur an: die bekannte dreischichtige Architektur. Sobald sie keine Domain-Schicht (hier die Business-Schicht) aufrufen, sind Business, Core und Domain alle gleich. In jedem Fall ist dies die Schicht, in der sich die GeschĂ€ftslogik befindet, und wenn dies von der Datenschicht abhĂ€ngt, bedeutet dies, dass einige Konzepte aus der Datenschicht irgendwie in die DomĂ€nenschicht fließen und diese verunreinigen.



Die vierschichtige Architektur ist im Wesentlichen dieselbe, die DomÀnenschicht hÀngt immer noch davon ab, und da sie davon abhÀngt, werden unnötige AbhÀngigkeiten von Drittanbietern ihren Weg dorthin finden.



Und in diesem Sinne gibt es eine Architektur, mit der dies vermieden werden kann - es ist eine Zwiebelarchitektur („Zwiebel“). Der Unterschied besteht darin, dass es aus konzentrischen Schichten besteht, die AbhĂ€ngigkeiten gehen von außen nach innen. Das heißt, die Ă€ußere Schicht kann von irgendwelchen inneren abhĂ€ngen, die innere Schicht kann nicht von den Ă€ußeren abhĂ€ngen.

Die Ă€ußerste Schicht ist die BenutzeroberflĂ€che im globalen Sinne (das heißt, es handelt sich nicht unbedingt um eine menschliche BenutzeroberflĂ€che, es kann sich um eine REST-API oder etwas anderes handeln). Und die Infrastruktur, die im Allgemeinen oft auch wie E / A aussieht, ist dieselbe Datenbank, in der Tat eine Datenschicht. All diese Dinge befinden sich in der Ă€ußeren Schicht. Das heißt, aufgrund dessen die Anwendung einige Daten, Befehle usw. empfĂ€ngt, werden sie entfernt und die DomĂ€nenschicht wird von der AbhĂ€ngigkeit von diesen Dingen befreit.

Als nÀchstes kommt die Anwendungsschicht - ein ziemlich ganzheitliches Thema, aber dies ist die Schicht, in der sich Skripte und BenutzerfÀlle befinden. Diese Schicht verwendet die DomÀnenschicht, um ihre Konzepte zu implementieren.

In der Mitte befindet sich die DomÀnenschicht. Wie wir sehen, hÀngt er nicht mehr von irgendetwas ab, er wird eine Sache in sich. Und deshalb wird die DomÀnenschicht oft als "Kern" bezeichnet, weil es der Kern ist, der im Zentrum steht, der nicht von Dingen Dritter abhÀngt.



Eine der Optionen zur Implementierung einer solchen Zwiebelarchitektur ist die hexagonale Architektur oder „Ports und Adapter“. Ich habe dieses Bild zur EinschĂŒchterung mitgebracht, ich werde nicht darĂŒber sprechen. Am Ende des Beitrags befindet sich ein Link zu einem von einer Million Artikeln ĂŒber diese Architektur, die Sie lesen können.

Ein bisschen ĂŒber taktische Muster: Separated Interface


Wie ich bereits sagte, sind zum einen die meisten taktischen Muster jedem bekannt, und zum anderen geht es in meinem Bericht darum, dass sie nicht die Essenz sind. Aber ich mag das Muster der getrennten Schnittstelle separat und ich möchte separat darĂŒber sprechen.

Kehren wir zum Code unseres Microservices zurĂŒck und sehen, was mit dem Repository passiert ist.



Die DomÀnenschicht hatte die Repository- Schnittstelle IOrdersRepository.cs und deren Implementierung OrdersRepository.cs.

 using System.Linq; namespace DotNext.Sales.Core { public interface IOrdersRepository { Order GetOrder(long id); void SaveOrder(Order order); IQueryable<Order> GetOrders(); #region V2 int GetLast3YearsCompletedOrdersCountFor(long customerId); #endregion } } 


Hier haben wir eine bestimmte Methode zum Empfangen von Bestellungen fĂŒr die letzten drei Jahre hinzugefĂŒgt. GetLast3YearsCompletedOrdersCountFor.

Und sie haben es in irgendeiner Form implementiert (in diesem Fall ĂŒber das Entity Framework, aber es kann alles sein):

  public int GetLast3YearsCompletedOrdersCountFor(long customerId) { var threeYearsAgo = DateTime.UtcNow.AddYears(-3); return _dbContext.Orders .Count(o => o.CustomerId == customerId && o.State == OrderState.Completed && o.OrderDate >= threeYearsAgo); } 


Sehen Sie, was das Problem ist. Das Repository landete in der DomÀnenschicht, seine Implementierung in der DomÀnenschicht, aber der Code, beginnend mit DateTime.UtcNow.AddYears (-3), gehört nicht von Natur aus zur DomÀnenschicht und ist keine GeschÀftslogik. Ja, LINQ macht es mehr oder weniger humanisiert, aber wenn es hier zum Beispiel SQL-Abfragen gÀbe, wÀre alles völlig traurig.

Die Bedeutung des Musters der getrennten Schnittstelle besteht darin, dass die Dienstschnittstelle, die wir in der DomÀnenlogik verwenden, in der DomÀnenschicht deklariert wird. Wir sprechen von Repositorys und Àhnlichen Diensten, bei denen die Details der Implementierung dieser Dienste keine GeschÀftslogik sind. Die GeschÀftslogik ist die Tatsache, dass diese Dienste vorhanden sind und dass sie in der DomÀnenschicht aufgerufen und verwendet werden. Daher verbleibt die Repository-Schnittstelle in der DomÀnenschicht, und die Implementierung wird in die Infrastrukturschicht verschoben.

Ich habe eine andere Option vorbereitet. Die Repository-Schnittstelle verbleibt in der Core-Assembly, die Implementierung wird jedoch in Infrastructure.EF verschoben.



Daher haben wir die Konzepte, die der DomÀnenschicht nicht eigen waren, in die Infrastruktur aufgenommen. Als Nebeneffekt können wir diese Infrastruktur durch eine andere Implementierung ersetzen. Dies ist jedoch nicht das Hauptziel, sondern, wie gesagt, die DomÀnenlogik von AbhÀngigkeiten von Drittanbietern zu befreien.

Noch einmal ĂŒber die Sprache


Reden wir immer wieder ĂŒber die Sprache.

Zu Beginn haben wir das Domain-Modell „Speaker-Talk-Event“ erstellt. Ich denke, dass niemand spezielle Fragen aufgeworfen hat.

Und hier ist das Szenario, auf dessen Grundlage wir dieses DomÀnenmodell erstellt haben:



Das Skript ist in Russisch und das Domain-Modell in Englisch.

FĂŒr nicht englischsprachige Entwickler ist dies etwas, mit dem Sie stĂ€ndig leben mĂŒssen.



Jeder von Ihnen fĂŒhrt diesen Prozess höchstwahrscheinlich stĂ€ndig durch: ĂŒbersetzt vom Russischen ins Englische und umgekehrt. Diejenigen, die mit englischsprachigen Kunden und Projekten arbeiten, sind etwas einfacher, da die Anforderungen auf Englisch sind, GesprĂ€che mit Kunden auf Englisch, in der Regel alle Szenarien auf Englisch, der Code auf Englisch und nur die Kommunikation innerhalb des Teams auf Russisch, die schnell auf Englisch wĂ€chst (Kunde - Kunde, Bestellung - Bestellung). Und diese kognitive Belastung, dieser Overhead, der durch eine stĂ€ndige Übersetzung entsteht, lĂ€sst ein wenig nach.

, , , . , .

1, . , — , , .



1. PascalCase , , , , , , , - - .

, - ?



, use case, , . , . C#, , , . , , , .

, , Domain-Driven Design. , , , , C# . .

, - Continuous Integration. , , , - - . , - , , . , 95% , , Continuous Integration, , TeamCity . .

, . Nein. , 1-, , . «» , . , , .



, Domain-Driven Design.

— , . Domain-Driven Design — , . — , , ubiquitous language. , , . , , , .

Weiter. , . - , , , , , , , , , , — . .

. . . . , , . DSL-. .NET-, - , , , , . , .

, - . , , ubiquitous language -. .



, , GitHub .

DotNext:
, « , ». DotNext ( 15-16 ) . , 1 , .

Source: https://habr.com/ru/post/de440772/


All Articles