Znajdź Aggregate Root DDD
On 1 lutego, 2021 by adminZagrajmy w ulubioną grę wszystkich, znajdź Aggregrate Root. Skorzystajmy z kanonicznej domeny problemu Customer / Order / OrderLines / Product. Tradycyjnie, Customer, Order i Product to AR, a OrderLines to jednostki pod Order. Logika jest taka, że musisz zidentyfikować klientów, zamówienia i produkty, ale linia zamówienia nie istniałaby bez zamówienia. Dlatego w naszej domenie problemów mamy regułę biznesową mówiącą, że klient może mieć tylko jedno niedostarczone zamówienie na raz.
Czy to powoduje przeniesienie zamówienia pod zagregowany katalog główny klienta? Myślę, że tak. Ale czyniąc to, powoduje to, że AR Klienta jest raczej duży i później może wystąpić problem z współbieżnością.
A co by było, gdybyśmy mieli regułę biznesową, która mówi, że klient może zamówić określony produkt tylko raz w całym jego życiu. To kolejny dowód na to, że klient jest właścicielem zamówienia.
Ale jeśli chodzi o do wysyłki, wszystkie działania związane z Zamówieniem wykonują oni, a nie klient. To trochę głupie, aby załadować całego klienta, aby oznaczyć pojedyncze Zamówienie jako dostarczone.
To jest co proponuję:
class Customer { public Guid Id {get;set;} public string Name { get; set; } public Address Address { get; set; } public IEnumerable<Order> Orders { get; set; } public void PlaceOrder(ThingsInTheOrder thingsInTheOrder) { // Make sure there aren"t any pending orders already. // Make sure they aren"t ordering a Widget if they"ve already ordered a Widget in the past. // Create an Order object and add it to the collection. Raise a domain event to trigger emails and other stuff } } class Order { public Guid Id { get; set; } public IEnumerable<OrderLine> OrderLines { get; set; } public ShippingData {get;set;} public void Ship(ShippedByPerson shippedByPerson, string trackingCode) { // Create a new ShippingData object and assign it from the data passed in. // Publish a domain event } }
Moim największym zmartwieniem jest kwestia współbieżności i fakt, że samo zamówienie ma charakterystyczne s agregatu.
Odpowiedź
Jakie są kryteria definiowania agregatu?
Wróćmy do podstaw wielkiej niebieskiej książki:
Aggregate: Zbiór powiązanych obiektów, które są traktowane jako jednostka na potrzeby zmian danych . Odnośniki zewnętrzne są ograniczone do jednego członka AGREGATU, oznaczonego jako root. W granicach AGREGATÓW obowiązuje zestaw reguł spójności.
Celem jest zachowanie niezmienników. Ale chodzi również o właściwe zarządzanie tożsamością lokalną, tj. Identyfikacją obiektów, które same w sobie nie mają znaczenia.
Order
i Order line
definitywnie należą do takiego klastra. Na przykład:
- Usuń
Order
, będzie wymagało usunięcia wszystkich jego wierszy. - Usunięcie wiersza może wymagać zmiany numeracji kolejnych wierszy.
- Dodanie nowego wiersza wymagałoby określenia wartości zerowej wiersza w oparciu o wszystkie inne wiersze w tej samej kolejności.
- Zmiana niektórych informacji o zamówieniu, takich jak na przykład waluta, może wpłynąć na znaczenie ceny w elementach zamówienia (lub wymagać ponownego obliczenia cen).
Więc tutaj pełne zagregowane jest wymagane, aby zapewnić spójność reguł i niezmienników.
Kiedy przestać?
Teraz opiszesz niektóre reguły biznesowe i argumentujesz, że aby je zapewnić, musisz wziąć pod uwagę klienta jako część agregatu:
Mamy biznes reguła mówiąca, że Klient może mieć tylko jedno niedostarczone zamówienie naraz.
Oczywiście, czemu nie. Zobaczmy konsekwencje: dostęp do zamówienia byłby zawsze możliwy za pośrednictwem klienta. Czy to prawdziwe życie? Kiedy pracownicy wypełniają pola w celu dostarczenia zamówienia, czy będą musieli odczytać kod kreskowy klienta i kod kreskowy zamówienia, aby uzyskać dostęp do zamówienia W rzeczywistości, ogólnie rzecz biorąc, tożsamość Zamówienia jest globalna, a nie lokalna dla klienta, a ta względna niezależność sugeruje trzymanie go poza agregatem.
Ponadto te reguły biznesowe wyglądają bardziej jak zasady: jest to arbitralna decyzja firmy, aby przeprowadzić proces zgodnie z tymi zasadami. Jeśli zasady nie są przestrzegane, szef może być niezadowolony, ale dane nie są tak naprawdę niespójne. Co więcej, w nocy ” na klienta jednorazowo jedno niedostarczone zamówienie ” może stać się ” dziesięć niedostarczonych zamówień na klienta ” lub nawet ” niezależnie od klienta, sto niedostarczonych zamówień na magazyn „, aby agregat nie był już uzasadniony.
Odpowiedź
Krótka wersja
Uzasadnieniem dla DDD jest to, że obiekty domeny są abstrakcjami, które powinny spełniać wymagania dotyczące domeny funkcjonalnej – jeśli obiekty domeny nie są w stanie łatwo spełnić tych wymagań, sugeruje to, że używasz złej abstrakcji.
Nazewnictwo Obiekty domeny używające rzeczowników encji mogą prowadzić do tego, że te obiekty stają się ściśle ze sobą powiązane i stają się rozdętymi obiektami „bogów”, a także mogą wywoływać problemy, takie jak ten w tym pytaniu, np. jako „Gdzie jest właściwe miejsce na str ut metoda CreateOrder? ”.
Aby ułatwić identyfikację „właściwego” zagregowanego katalogu głównego, rozważ inne podejście, w którym obiekty domeny są oparte na funkcjonalnych wymaganiach biznesowych wysokiego poziomu – tj.wybierz rzeczowniki, które nawiązują do wymagań funkcjonalnych i / lub zachowań, które muszą spełniać użytkownicy systemu.
Długa wersja
DDD to podejście do OO Design, którego celem jest utworzenie wykresu Domain Objects w Business Warstwa twojego systemu – obiekty domeny są odpowiedzialne za spełnienie wymagań biznesowych wysokiego poziomu i najlepiej byłoby, gdybyś mógł polegać na warstwie danych w takich kwestiach, jak wydajność i integralność bazowego stałego magazynu danych.
Innym sposobem spojrzenia na to mogą być punkty na tej liście.
- Rzeczowniki jednostek zwykle sugerują atrybuty danych.
- Rzeczowniki domeny powinny sugerować zachowanie
- Modelowanie DDD i OO dotyczy abstrakcji opartych na wymaganiach funkcjonalnych i podstawowej logice domeny / biznesowej.
- Warstwa logiki biznesowej jest odpowiedzialna za spełnienie wymagań domeny wysokiego poziomu
Jednym z powszechnych nieporozumień dotyczących DDD jest to, że obiekty domeny powinny opierać się na fizycznych „rzecz” świata (tj. jakiś rzeczownik, na który można wskazać w prawdziwym świecie, któremu przypisuje się wszelkiego rodzaju dane / właściwości), jednak dane / atrybuty tych rzeczy ze świata rzeczywistego niekoniecznie stanowią dobry punkt wyjścia przy próbie ustalenia obniżone wymagania funkcjonalne.
Oczywiście logika biznesowa powinna używać tych danych, ale same obiekty domeny powinny ostatecznie być abstrakcjami, które reprezentują funkcjonalne wymagania i zachowania domeny.
Na przykład; rzeczowniki, takie jak Order
lub Customer
, nie oznaczają żadnego zachowania i dlatego są ogólnie nieprzydatnymi abstrakcjami do reprezentowania logiki biznesowej i obiektów domeny.
Szukając rodzajów abstrakcji, które mogą być przydatne do reprezentowania logiki biznesowej, weź pod uwagę typowe wymagania, których możesz oczekiwać od systemu:
- Jako sprzedawca, ja chcę utworzyć zamówienie dla nowego klienta, aby móc wygenerować fakturę za sprzedawane produkty wraz z ich cenami i ilością.
- Jako doradca ds. obsługi klienta chcę anulować oczekujące zamówienie, aby Zamówienie nie zostało zrealizowane przez operatora magazynu.
- Jako doradca ds. Obsługi klienta chcę zwrócić linię zamówienia, aby można było dostosować produkt do zapasów, a płatność zostanie zwrócona za pośrednictwem pierwotnej płatności klienta metoda.
- Jako operator magazynu chcę wyświetlić wszystkie produkty w oczekującym zamówieniu oraz informacje o wysyłce, aby móc wybrać produkty i wysłać je kurierem.
- itp.
Modelowanie wymagań domeny z podejściem DDD
Na podstawie powyższej listy rozważ potencjalne cele domeny ts dla takiego systemu zamówień:
SalesOrderCheckout PendingOrdersStream WarehouseOrderDespatcher OrderRefundProcessor
Jako obiekty domeny, reprezentują one abstrakcje, które przejmują własność różnych behawioralnych wymagań domeny; w rzeczywistości ich rzeczowniki silnie wskazują na określone wymagania funkcjonalne, które spełniają.
(Może tam być również dodatkowa infrastruktura, taka jak EventMediator
do spełnienia powiadomienia dla obserwatorów, którzy chcą wiedzieć, kiedy zostało utworzone nowe zamówienie lub kiedy zamówienie zostało wysłane itp.).
Na przykład SalesOrderCheckout
prawdopodobnie musi przetwarzać dane o klientach, wysyłce i produktach, jednak nie ma to nic wspólnego z zachowaniem zamówień wysyłkowych, sortowaniem oczekujących zamówień lub zwrotami kosztów.
Dla SalesOrderCheckout
w celu spełnienia wymagań domeny obejmuje egzekwowanie tych reguł biznesowych, takich jak zapobieganie zamawianiu zbyt wielu elementów przez klientów, ewentualnie przeprowadzanie weryfikacji i być może wysyłanie powiadomień o innych częściach systemu – może robić to wszystko bez konieczności polegania na dowolne inne obiekty.
DDD używające rzeczowników encji do reprezentowania obiektów domeny
Ther Istnieje szereg potencjalnych niebezpieczeństw związanych z prostymi rzeczownikami, takimi jak Order
, Customer
i Product
jako obiekty domeny; wśród tych problemów są te, do których nawiązujesz w pytaniu:
- Jeśli metoda obsługuje
Order
,Customer
iProduct
, do którego obiektu domeny należy? - Gdzie jest główny katalog zbiorczy dla tych 3 obiektów?
Jeśli wybierzesz rzeczowniki jednostek do reprezentowania obiektów domeny, może się wydarzyć wiele rzeczy:
-
Order
, iProduct
są narażone na ryzyko przekształcenia się w obiekty„ bogów ” - Ryzyko uzyskania pojedynczego
Manager
boski obiekt, aby powiązać wszystko razem. - Te obiekty mogą zostać ze sobą ściśle powiązane – spełnienie wymagań domeny bez przejścia
this
(lubself
) - Ryzyko powstania „nieszczelnych” abstrakcji – tj.obiekty domeny mają ujawnić dziesiątki metod
get
/set
, które osłabiają hermetyzację (lub, jeśli tego nie zrobisz, inny programista prawdopodobnie później …). - Ryzyko, że Obiekty Domeny staną się rozdęte złożoną mieszanką danych biznesowych (np. danych wprowadzanych przez użytkownika za pośrednictwem interfejsu użytkownika) i stanem przejściowym (np. „historia” działań użytkownika kiedy zamówienie zostało zmodyfikowane).
DDD, OO Design and Plain Models
Powszechnym błędnym przekonaniem dotyczącym DDD i OO Design jest to, że „zwykłe” modele są w jakiś sposób ” zły ”lub„ anty-wzorzec ”. Martin Fowler napisał artykuł opisujący Anemiczny Model domeny – ale jak wyjaśnia w artykule, samo DDD powinno nie „zaprzeczać” podejściu czystego oddzielenia warstw
„Warto również podkreślić, że umieszczanie zachowania w obiektach domeny nie powinno być sprzeczne solidne podejście do stosowania warstw do sepa oceniaj logikę domeny na podstawie takich rzeczy, jak wytrwałość i odpowiedzialność za prezentację. Logika, która powinna znajdować się w obiekcie domeny, to logika domeny – walidacje, obliczenia, reguły biznesowe – jakkolwiek chcesz to nazywać. „
Innymi słowy, używanie zwykłych modeli do przechowywania danych biznesowych przesyłanych między innymi warstwami (np. Modelu zamówienia przekazanego przez aplikację użytkownika, gdy użytkownik chce utworzyć nowe zamówienie) nie jest tym samym, co „model domeny anemicznej”. „Zwykłe” modele danych są często najlepszym sposobem śledzenia danych i przesyłania danych między warstwami (takimi jak usługa internetowa REST, magazyn trwałości, aplikacja lub interfejs użytkownika itp.).
Logika biznesowa może przetwarzać dane w tych modelach i mogą śledzić je jako część stanu biznesowego – ale niekoniecznie przejmują na własność te modele.
Aggregate Root
Patrząc ponownie na przykład Domain Objects – SalesOrderCheckout
, PendingOrdersStream
, WarehouseOrderDespatcher
, OrderRefundProcessor
nadal nie ma oczywistego Aggregate Root, ale tak jest esn „nie ma znaczenia, ponieważ te Obiekty Domeny mają dziko oddzielne obowiązki, które nie wydają się nakładać na siebie.
Funkcjonalnie nie ma potrzeby, aby SalesOrderCheckout
rozmawiał z PendingOrdersStream
, ponieważ jego praca jest zakończona, gdy doda nowe zamówienie do bazy danych; z drugiej strony PendingOrdersStream
może pobierać nowe zamówienia z bazy danych. Te obiekty nie muszą w rzeczywistości wchodzić w interakcje z każdym inne bezpośrednio (być może Mediator zdarzeń może dostarczać powiadomienia między nimi, ale spodziewałbym się, że jakiekolwiek sprzężenie między tymi obiektami będzie bardzo luźne)
Być może Aggregate Root będzie kontenerem IoC, który wstrzykuje jeden lub więcej te obiekty domeny do kontrolera interfejsu użytkownika, dostarczając również inną infrastrukturę, taką jak EventMediator
i Repository
. A może będzie to jakiś rodzaj lekkiej usługi Orchestrator znajdującej się na szczycie warstwy biznesowej.
Agregat główny nie musi być koniecznie obiektem domeny. Aby zachować separację problemów między obiektami domeny, ogólnie rzecz biorąc, dobrze jest, gdy agregacja root to oddzielny obiekt bez logiki biznesowej.
Komentarze
- Głosowałem negatywnie, ponieważ Twoja odpowiedź łączy koncepcje z Entity Framework, która jest technologią specyficzną dla firmy Microsoft, z Projektem opartym na domenie, która pochodzi z książki napisany przez Erica Evansa o tym samym nazwisku. W swojej odpowiedzi masz pewne stwierdzenia, które są bezpośrednio sprzeczne z książką DDD, a to pytanie nie wspomina o Entity Framework, ale konkretnie jest oznaczone tagiem DDD. W pytaniu nie ma też ' żadnej wzmianki o trwałości, więc nie ' nie sprawdzam, gdzie są odpowiednie tabele bazy danych.
- @RibaldEddie Dziękuję za poświęcenie czasu na przejrzenie odpowiedzi i komentarza. Zgadzam się, że wzmianka o trwałych danych nie ' tak naprawdę nie musi być w odpowiedzi, więc ' przeredagowaliśmy go, aby to usunąć. Główny punkt odpowiedzi można podsumować jako ” Rzeczowniki jednostek często nie są ' t bardzo dobre nazwy klas obiektów domeny ze względu na ich tendencję do stają się ciasno powiązanymi, rozdętymi obiektami Boga „, mam nadzieję, że przesłanie i uzasadnienie wymagań / zachowania funkcjonalnego WRT są teraz jaśniejsze?
- Książka DDD nie ' nie mam tej koncepcji IIRC. Ma jednostki, które są po prostu klasami, które po utworzeniu mają trwałą i unikalną tożsamość, tak że dwie oddzielne instancje oznaczają dwie unikalne i trwałe rzeczy, co kontrastuje z obiektami wartości, które nie ' nie mieć żadnej tożsamości i nie ' nie przetrwać w czasie. W książce zarówno obiekty Entities, jak i Value są obiektami domeny.
Odpowiedź
w naszej problematycznej domenie mamy zasada biznesowa mówiąca, że klient może mieć tylko jedno niedostarczone zamówienie na raz.
Zanim zagłębisz się w tę króliczą nory, powinieneś zapoznać się z Greg Dyskusja Younga na temat spójności zestawu , aw szczególności:
Jaki wpływ na biznes ma awaria?
Ponieważ w wielu przypadkach właściwą odpowiedzią nie jest zapobieganie dzieje się coś złego, ale zamiast tego wygenerować raporty wyjątków , gdy może wystąpić problem.
Ale zakładając, że wiele niedostarczonych zamówień jest znaczące obciążenie dla Twojej firmy ….
Tak, jeśli chcesz mieć pewność, że jest tylko jedno niedostarczone zamówienie, to musi istnieć jakiś agregat, który może zobaczyć wszystkie zamówienia klienta .
Ten agregat niekoniecznie jest agregatem klienta .
Może to być coś w rodzaju kolejki zamówień lub historii zamówień, w której wszystkie zamówienia dla określonego klient trafia do tej samej kolejki. Z tego, co powiedziałeś, nie potrzebuje on wszystkich danych profilu klienta, więc nie powinien być częścią tego zagregowanego.
Ale jeśli chodzi o wysyłkę, wszystkie czynności związane z zamówieniem wykonują oni, a nie klient.
Tak, kiedy faktycznie pracujesz nad realizacją i pull sheet, widok historii nie jest szczególnie istotny.
Widok historii, aby wymusić niezmiennik, potrzebuje tylko identyfikatora zamówienia i jego aktualnego stanu przetwarzania. To niekoniecznie musi być częścią tego samego agregatu co zamówienie – pamiętaj, granice agregatów dotyczą zarządzania zmianą, a nie strukturyzowania widoków.
Może więc być tak, że zamówienie będzie traktowane jako suma i historię zamówień jako oddzielną agregację i koordynuj działania między nimi.
Odpowiedź
Skonfigurowałeś przykład słomianej osoby. Jest to zbyt uproszczone i wątpię, czy odzwierciedla system ze świata rzeczywistego. Z tego powodu nie modelowałbym tych Jednostek i ich powiązanych zachowań w sposób, który określiłeś.
Twoje zajęcia muszą modelować stan zamówienia w sposób odzwierciedlony w wielu agregatach. Na przykład, gdy klient przestawia system w stan, w którym zamówienie klienta musi zostać przetworzone, mogę utworzyć agregację obiektu jednostki domeny o nazwie CustomerOrderRequest
lub PendingCustomerOrder
lub nawet CustomerOrder
lub w jakimkolwiek języku używanym przez firmę i może zawierać wskaźnik zarówno do klienta, jak i do linii zamówienia, a następnie mieć metodę taką jak canCustomerCompleteOrder()
, który jest wywoływany z warstwy usług.
Ten obiekt domeny zawierałby logikę biznesową określającą, czy zamówienie było prawidłowe.
Jeśli zamówienie było prawidłowe i przetworzone, to miałbym jakiś sposób na przeniesienie tego obiektu do innego obiektu, który reprezentował przetworzone zamówienie.
Myślę, że problem ze zrozumieniem polega na tym, że „używasz sztucznej nadmiernie uproszczony przykład agregatów. PendingOrder
może być własnym agregatem oddzielnie od UndeliveredOrder
i ponownie oddzielonym od lub CancelledOrder
lub cokolwiek innego.
Komentarze
- Chociaż twoja próba ponieważ język neutralny pod względem płci jest zabawny, chciałbym zauważyć, że kobiety nigdy nie stoją na polach, aby odstraszyć wrony.
- @RobertHarvey to ' to dziwna rzecz, na której należy się skupić w moim poście. Strachy na wróble i podobizny regularnie pojawiały się w postaci kobiecej w całej historii.
- Nie ' nie zrobiłbyś tego rozróżnienia w swoim poście, gdybyś nie ' nie uważaj tego za ważne. Jeśli chodzi o językoznawstwo, termin to ” słomkowy mężczyzna; ” wszelkie zastrzeżenia dotyczące seksizmu są prawie na pewno pokonane przez o czym on u diabła mówi ” czynnik stworzony przez wymyślenie własnego terminu.
- @RobertHarvey, jeśli ktoś wie, jaka słomka człowiek oznacza, że ' na pewno potrafią zrozumieć, co słychać ma osoba, jeśli nie ' nie słyszeli tego terminu. Czy możemy skupić się na treści mojego posta, proszę napisać oprogramowanie?
Odpowiedź
Vaughn Vernon wspomina o tym w swoim książka „Implementing Domain-Driven Design” na początku rozdziału 7 (Usługi):
„Często najlepszym wskazaniem, że należy utworzyć usługę w modelu domeny, jest sytuacja, gdy operacja, którą należy wykonać, wydaje się miejsca jako metoda na Agregacie lub obiekcie wartości ”.
W tym przypadku może istnieć usługa domeny o nazwie „CreateOrderService”, która pobiera instancję klienta i listę elementów zamówienia.
class CreateOrderService { public Order CreateOrder(Customer customer, ThingsInTheOrder thingsInTheOrder) { // Get all the orders for the customer // Check if any of the things to be ordered exist in previous orders // If none have been previously ordered, create the order and return it // Otherwise return null } }
Komentarze
- Czy możesz wyjaśnić, w jaki sposób usługa domeny może pomóc w rozwiązaniu problemu dotyczącego współbieżności w pytaniu?
Dodaj komentarz