Najděte kořenový adresář DDD
On 1 února, 2021 by adminPojďme si zahrát oblíbenou hru každého, najděte kořen Aggregrate. Pojďme použít kanonickou doménu problému Zákazník / Objednávka / OrderLines / Produkt. Tradičně jsou AR, Zákazník, Objednávka a Produkt, přičemž OrderLines jsou entitami v rámci Objednávky. Logika toho spočívá v tom, že musíte identifikovat zákazníky, objednávky a produkty, ale OrderLine by bez objednávky neexistoval. V naší problémové doméně tedy máme obchodní pravidlo, které říká, že zákazník může mít pouze jednu nedoručenou objednávku najednou.
Posunuje to objednávku pod kořenem agregace zákazníků? Myslím, že ano. Ale díky tomu je Customer AR poměrně velký a později může dojít k problémům se souběžností.
Nebo co kdybychom měli obchodní pravidlo uvádějící, že si zákazník může objednat konkrétní produkt pouze jednou za dobu jeho životnosti. To je více důkazů vyžadujících od zákazníka, aby vlastnil objednávku.
Ale když to přijde k přepravě provádějí všechny své akce na objednávce, nikoli na zákazníkovi. Je trochu hloupé, že musí načíst celého zákazníka, aby mohl označit jednotlivou objednávku jako doručenou.
Toto je co navrhuji:
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 } }
Největší mě znepokojuje otázka souběžnosti a skutečnost, že samotná Řád má charakteristické rysy s kořene agregátu.
Odpověď
Jaká jsou kritéria pro definování agregátu?
Vraťme se k základům velké modré knihy:
Agregát: Klastr přidružených objektů, které jsou za účelem změn dat považovány za jednotku . Externí odkazy jsou omezeny na jednoho člena AGGREGATE, označeného jako root. Sada pravidel konzistence platí v rámci hranic AGREGÁTU.
Cílem je zachovat invarianty. Jde ale také o řádnou správu místní identity, tj. Identifikaci objektů, které nemají samotný význam.
Order
a Order line
definitivně patří do takového klastru. Například:
- Smazat
Order
, bude vyžadovat vymazání všech jeho řádků. - Smazání řádku může vyžadovat přečíslování následujících řádků
- Přidání nového řádku by vyžadovalo určení čísla řádku na základě všech ostatních řádků ve stejném pořadí.
- Změna některých informací o objednávce, například měny, může ovlivnit význam ceny v řádkových položkách (nebo vyžadovat přepočet cen).
Takže zde celý souhrn je vyžadováno pro zajištění pravidel konzistence a invarianty.
Kdy přestat?
Nyní popíšete některá obchodní pravidla a tvrdíte, že k jejich zajištění je třeba vzít v úvahu zákazníka jako součást agregátu:
Máme busines Pravidlo, že zákazník může mít pouze jednu nedoručenou objednávku najednou.
Samozřejmě, proč ne. Podívejme se na důsledky: k objednávce bude vždy přístup prostřednictvím zákazníka. Je to skutečný život? Když pracovníci vyplňují políčka pro doručení objednávky, budou pro přístup k objednávce potřebovat přečíst čárový kód zákazníka a čárový kód objednávky „Obecně platí, že identita Objednávky není pro zákazníka globální, nýbrž lokální, a tato relativní nezávislost naznačuje, že by měl zůstat mimo agregát.
Kromě toho tato obchodní pravidla vypadají spíše jako zásady: je to svévolné rozhodnutí společnosti zahájit proces s těmito pravidly. Pokud pravidla nejsou dodržována, šéf může být nešťastný, ale data nejsou ve skutečnosti nekonzistentní. A navíc, přes noc “ na zákazníka by se jedna nedoručená objednávka po “ mohla stát “ deset nedoručených objednávek na zákazníka “ nebo dokonce “ nezávisle na zákazníkovi, sto nedoručených objednávek na sklad „, takže agregace již nemusí být oprávněná.
Odpověď
Krátká verze
Důvodem DDD je, že doménové objekty jsou abstrakce, které by měly splňovat vaše požadavky na funkční doménu – pokud doménové objekty nejsou schopny tyto požadavky snadno splnit, naznačuje to, že používáte nesprávnou abstrakci.
Pojmenování Doménové objekty pomocí podstatných jmen entit mohou vést k tomu, že se tyto objekty navzájem pevně spojí a stanou se nafouknutými „božskými“ objekty, a mohou vyhodit problémy, jako je ten v této otázce, například jako „Kde je správné místo pro p Metoda CreateOrder? „.
Chcete-li usnadnit identifikaci „správného“ agregačního kořene, zvažte jiný přístup, kdy jsou doménové objekty založeny na funkčních obchodních požadavcích na vysoké úrovni – tj.zvolit podstatná jména, která se zmiňují o funkčních požadavcích a / nebo chování, které uživatelé systému musí provádět.
Dlouhá verze
DDD je přístup k OO Design, jehož cílem je vygenerovat graf Doménové objekty v Obchodě Vrstva vašeho systému – Doménové objekty jsou odpovědné za splnění vašich obchodních požadavků na vysoké úrovni a v ideálním případě by měly být schopny spolehnout se na datovou vrstvu pro věci, jako je výkon a integrita podkladového trvalého úložiště dat.
Dalším způsobem, jak se na to podívat, mohou být odrážky v tomto seznamu.
- Podstatná jména entit obvykle navrhují atributy dat.
- Podstatná jména domény by měla navrhovat chování
- DDD a OO Modeling se zabývají abstrakcemi založenými na funkčních požadavcích a základní doménové / obchodní logice.
- Vrstva obchodní logiky je odpovědná za splnění požadavků na vysoké úrovni domény.
Jednou z běžných mylných představ o DDD je, že doménové objekty by měly být založeny na některých fyzických světová „věc“ (tj. nějaké podstatné jméno, na které můžete v reálném světě poukazovat, připisované všem druhům dat / vlastností), avšak data / atributy těchto věcí ve skutečném světě nemusí nutně být dobrým výchozím bodem při pokusu o hřebík dolů funkční požadavky.
Business Logic by samozřejmě měla používat tato data, ale samotné doménové objekty by měly být v konečném důsledku abstrakce, které představují funkční požadavky a chování domény.
Například; Podstatná jména jako Order
nebo Customer
nenaznačují žádné chování, a proto jsou obecně neužitečnými abstrakcemi pro reprezentaci obchodní logiky a doménových objektů.
Při hledání druhů abstrakcí, které by mohly být užitečné pro reprezentaci obchodní logiky, zvažte typické požadavky, které můžete očekávat od systému:
- Jako prodejce jsem chci vytvořit objednávku pro nového zákazníka, abych mohl vygenerovat fakturu za prodávané produkty s jejich cenami a množstvím.
- Jako poradce zákaznického servisu chci zrušit nevyřízenou objednávku, aby Objednávka není splněna provozovatelem skladu.
- Jako poradce zákaznického servisu chci vrátit řádek objednávky, aby bylo možné produkt upravit do inventáře a platba byla vrácena zpět prostřednictvím původní platby zákazníka. metoda.
- Jako provozovatel skladu chci zobrazit všechny produkty na nevyřízené objednávce a informace o přepravě, abych si mohl produkty vyzvednout a odeslat prostřednictvím kurýra.
- atd.
Modelování doménových požadavků s přístupem DDD
Na základě výše uvedeného seznamu zvažte některé potenciální doménové objekty Pro takový systém objednávek:
SalesOrderCheckout PendingOrdersStream WarehouseOrderDespatcher OrderRefundProcessor
Jako doménové objekty představují abstrakce, které převezmou vlastnictví různých požadavků na doménu chování; jejich podstatná jména skutečně silně naznačují konkrétní funkční požadavky, které splňují.
(Může tam být i další infrastruktura, například EventMediator
k předání oznámení pro pozorovatele, kteří chtějí vědět, kdy byla vytvořena nová objednávka nebo kdy byla objednávka odeslána, atd.)
Například SalesOrderCheckout
pravděpodobně potřebuje zpracovávat údaje o zákaznících, přepravě a produktech, ale netýká se nic společného s chováním u přepravních objednávek, třídění nevyřízených objednávek nebo vrácení peněz.
Pro SalesOrderCheckout
ke splnění svých doménových požadavků zahrnuje vynucování těchto obchodních pravidel, jako je zabránění zákazníkům objednávat příliš mnoho položek, možná spuštění nějaké validace a možná zvyšování oznámení pro jiné části systému – dokáže všechny tyto věci, aniž by nutně musel záviset na kterýkoli z ostatních objektů.
DDD používající podstatná jména entit k reprezentaci doménových objektů
Ther Existuje řada potenciálních nebezpečí při zacházení s jednoduchými podstatnými jmény, jako jsou Order
, Customer
a Product
jako doménové objekty; Mezi tyto problémy patří ty, na které se v otázce zmiňujete:
- Pokud metoda zpracovává
Order
, aCustomer
aProduct
ke kterému doménovému objektu patří? - Kde je agregovaný kořen pro tyto 3 objekty?
Pokud pro reprezentaci doménových objektů zvolíte podstatná jména entit, může se stát řada věcí:
-
Order
, aProduct
jsou vystaveni riziku, že z nich vyrostou„ božské “objekty - riziko, že skončíte s jediným
Manager
božský objekt spojit vše dohromady. - Tyto objekty mohou být navzájem pevně spojeny – může být obtížné splnit doménové požadavky bez předání
this
(neboself
) - riziko vzniku „děravých“ abstrakcí – tj.Očekává se, že objekty domény vystaví desítky
get
/set
metod, které oslabují zapouzdření (nebo, pokud tak neučiníte, pak nějaký jiný programátor pravděpodobně později …). - Riziko nadýmání doménových objektů se složitou směsí obchodních dat (např. zadávání uživatelských dat prostřednictvím uživatelského rozhraní) a přechodného stavu (např. „historie“ akcí uživatelů když byla objednávka upravena).
DDD, OO Design a Plain Models
Běžná mylná představa o DDD a OO Design je, že „obyčejné“ modely jsou nějakým způsobem “ bad „or“ anti-pattern „. Martin Fowler napsal článek popisující anemický doménový model – ale jak v článku objasňuje, samotné DDD by mělo není „v rozporu“ s přístupem čistého oddělení mezi vrstvami
„Je také třeba zdůraznit, že uvedení chování do doménových objektů by nemělo být v rozporu pevný přístup pomocí vrstvení k oddělení hodnotit logiku domény z takových věcí, jako je vytrvalost a odpovědnost za prezentaci. Logika, která by měla být v doménovém objektu, je logika domény – validace, výpočty, obchodní pravidla – jakkoli tomu chcete říkat. „
Jinými slovy, používání obyčejných modelů pro uchovávání obchodních dat přenášených mezi jinými vrstvami (např. Model objednávky předaný aplikací uživatele, když chce uživatel vytvořit novou objednávku) není to samé jako „anemický model domény“. „prosté“ datové modely jsou často nejlepším způsobem, jak sledovat data a přenášet data mezi vrstvami (jako je webová služba REST, úložiště persistence, aplikace nebo uživatelské rozhraní atd.).
Obchodní logika může zpracovávat data v těchto modelech a může je sledovat jako součást obchodního stavu – ale tyto modely nemusí nutně převzít.
Kořenový agregát
Znovu se podíváme na ukázkové doménové objekty – SalesOrderCheckout
, PendingOrdersStream
, WarehouseOrderDespatcher
, OrderRefundProcessor
stále není zřejmý agregovaný kořen, ale to ano Ve skutečnosti na tom nezáleží, protože tyto doménové objekty mají divoce oddělené odpovědnosti, které se nepřekrývají.
Funkčně není třeba, aby SalesOrderCheckout
mluvil s PendingOrdersStream
, protože práce bývalého je kompletní, když do databáze přidá novou objednávku; na druhou stranu PendingOrdersStream
může načíst nové objednávky z databáze. Tyto objekty ve skutečnosti nemusí s každým pracovat jiné přímo (Možná, že zprostředkovatel událostí může poskytovat oznámení mezi těmito dvěma, ale očekával bych, že jakékoli spojení mezi těmito objekty bude velmi volné)
Možná, že agregovaný kořen bude kontejnerem IoC, který vloží jeden nebo více tyto doménové objekty do řadiče uživatelského rozhraní a také dodávají další infrastrukturu, jako je EventMediator
a Repository
. Nebo to možná bude nějaká lehká služba Orchestrator, která bude sedět na vrcholu obchodní vrstvy.
Kořen agregátu „nemusí nutně být doménový objekt. Z důvodu zachování oddělení obav mezi doménovými objekty je obecně dobrá věc, když agregát root je samostatný objekt bez obchodní logiky.
Komentáře
- Hlasoval jsem proti, protože vaše odpověď spojuje koncepty z Entity Framework, což je technologie specifická pro Microsoft, s Domain Driven Design, který je z knihy napsal stejnojmenný Eric Evans. Ve své odpovědi máte některá prohlášení, která jsou v přímém rozporu s knihou DDD, a tato otázka se vůbec nezmiňuje o Entity Framework, ale konkrétně je označena DDD. V otázce také ‚ není vůbec žádná zmínka o perzistenci, takže ‚ nevidím, kde jsou relevantní databázové tabulky.
- @RibaldEddie Děkuji, že jste si našli čas na přezkoumání odpovědi a komentáře, souhlasím s tím, že zmínka o trvalých datech nemusí být v odpovědi ‚, takže já ‚ přeformuloval to, aby to odstranil. Hlavní zaměření odpovědi lze shrnout jako “ podstatná jména entit často nejsou ‚ velmi dobrá jména tříd doménových objektů kvůli jejich tendenci k stanete se pevně spojenými nafouknutými božskými objekty „, doufejme, že poselství a uvažování o funkčních požadavcích / chování WRT jsou nyní jasnější?
- Kniha DDD ‚ tento koncept IIRC nemají. Má entity, které jsou pouze třídami, které mají při instanci trvalou a jedinečnou identitu, takže dvě oddělené instance znamenají dvě jedinečné a trvalé věci, což kontrastuje s hodnotovými objekty, které don ‚ Nemáte žádnou identitu a nezůstávejte ‚ t v průběhu času. V knize jsou objekty Entity i Hodnoty objekty domény.
Odpověď
v naší problémové doméně máme obchodní pravidlo, které říká, že zákazník může mít pouze jednu nedoručenou objednávku najednou.
Než se dostanete příliš hluboko do králičí nory, měli byste zkontrolovat Grega Mladá diskuse o nastavení konzistence , zejména:
Jaký je obchodní dopad selhání?
Protože v mnoha případech se správnou odpovědí nelze pokusit zabránit došlo k nesprávné věci, ale místo toho generovat zprávy o výjimkách , pokud by mohl být problém.
Ale za předpokladu, že se jedná o více nedoručených objednávek významná odpovědnost pro vaše podnikání ….
Ano, pokud se chcete ujistit, že existuje pouze jedna nedoručená objednávka, pak musí existovat nějaký agregát, který může zobrazit všechny objednávky pro zákazníka .
Tento agregát nemusí být nutně agregátem zákazníka .
Může to být něco jako fronta objednávek nebo historie objednávek, kde jsou všechny objednávky pro konkrétní zákazník vstoupí do stejné fronty. Z toho, co jste řekli, není potřeba všech údajů profilu zákazníka, takže by neměla být součástí tohoto agregátu.
Pokud však jde o dopravu, provádějí všechny své akce na objednávce, nikoli na zákazníkovi.
Ano, když ve skutečnosti pracujete s plněním a vytáhnout listy, zobrazení historie není nijak zvlášť relevantní.
Zobrazení historie vyžaduje pro vynucení invariantu pouze ID objednávky a její aktuální stav zpracování. To nutně nemusí být součástí stejného agregátu jako objednávka – nezapomeňte, že hranice agregace jsou o správě změn, nikoli o strukturování pohledů.
Může se tedy stát, že s objednávkou zacházíte jako s agregátem a historii objednávek jako samostatný agregát a koordinovat aktivitu mezi nimi.
Odpověď
Nastavili jste příklad slaměné osoby. Je to příliš zjednodušující a pochybuji, že odráží systém reálného světa. Tyto entity a jejich související chování bych nemodeloval způsobem, který jste kvůli tomu zadali.
Vaše třídy musí modelovat stav objednávky způsobem, který se odráží ve více agregátech. Například když zákazník uvede systém do stavu, kdy je třeba zpracovat požadavek na objednávku zákazníka, mohl bych vytvořit agregát objektu entity domény s názvem CustomerOrderRequest
nebo PendingCustomerOrder
nebo dokonce jen CustomerOrder
nebo jakýkoli jazyk, který firma používá, a může obsahovat ukazatel jak na zákazníka, tak na OrderLines a poté mít metodu jako canCustomerCompleteOrder()
který se volá ze servisní vrstvy.
Tento doménový objekt by obsahoval obchodní logiku, která by určovala, zda je objednávka platná.
Pokud byla objednávka platná a zpracovaná, měl bych nějaký způsob, jak tento objekt převést na jiný objekt, který představoval zpracovanou objednávku.
Myslím, že problém s vaším porozuměním spočívá v tom, že používáte nepřirozený příliš zjednodušený příklad agregátů. PendingOrder
může to být vlastní agregát odděleně od UndeliveredOrder
a opět oddělen od nebo CancelledOrder
nebo cokoli jiného.
Komentáře
- Ačkoli váš pokus v genderově neutrálním jazyce je zábavný, poznamenal bych, že ženy nikdy nestojí na polích, aby vyděsily vrány.
- @RobertHarvey, že ‚ je zvláštní věc, na kterou se zaměřit v mém příspěvku. Strašáci a podobizny se pravidelně objevují v ženské podobě v celé historii.
- Pokud byste to neudělali, ‚ byste ve svém příspěvku nerozlišili 8dafa54eaa „>
nepovažuji to za důležité. V lingvistice jde o “ slaměný muž; “ jakékoli výhrady k sexismu téměř jistě převyšuje “ o čem to sakra mluví, o “ faktoru vytvořeném vymýšlením vlastního termínu.
Odpověď
Vaughn Vernon to zmiňuje ve svém kniha „Implementace designu řízeného doménou“ na začátku kapitoly 7 (Služby):
„Nejlepší známkou toho, že byste měli vytvořit službu v doménovém modelu, je často situace, kdy je potřeba provést operaci místa jako metoda na agregátu nebo hodnotovém objektu „.
Takže v tomto případě může existovat doménová služba s názvem „CreateOrderService“, která převezme instanci zákazníka a seznam položek pro objednávku.
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 } }
Komentáře
- Můžete mi prosím vysvětlit, jak může doménová služba v otázce pomoci při řešení problému souběžnosti?
Napsat komentář