Zoek de DDD Aggregate Root
Geplaatst op februari 1, 2021 door adminLaten we ieders favoriete spel spelen, zoek de Aggregrate Root. Laten we het canonieke probleemdomein Customer / Order / OrderLines / Product gebruiken. Traditioneel zijn klant, order en product de ARs, waarbij OrderLines entiteiten zijn onder de Order. De logica hierachter is dat u klanten, bestellingen en producten moet identificeren, maar een OrderLine zou niet bestaan zonder een Order. Dus in ons probleemdomein hebben we een bedrijfsregel die zegt dat een klant slechts één niet-bezorgde bestelling kan hebben per keer.
Verplaatst dat de order onder de aggregate root van de klant? Ik denk van wel. Maar door dat te doen, maakt dat de klant-AR vrij groot en later onderhevig aan problemen met gelijktijdigheid.
Of, wat als we een zakelijke regel hadden waarin stond dat een klant een bepaald product maar één keer in zijn leven kan bestellen. Dit is meer bewijs dat de klant eigenaar moet zijn van de bestelling.
Maar als het komt voor verzending, doen ze al hun acties op de bestelling, niet op de klant. Het is nogal dom om de hele klant te moeten laden om een individuele bestelling als afgeleverd te markeren.
Dit is wat ik voorstel:
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 } }
Mijn grootste zorg is de kwestie van gelijktijdigheid en het feit dat de Order zelf kenmerkende s van een geaggregeerde root.
Answer
Wat zijn de criteria voor het definiëren van een aggregaat?
Laten we teruggaan naar de basis van het grote blauwe boek:
Aggregate: Een cluster van bijbehorende objecten die worden behandeld als een eenheid met het oog op gegevenswijzigingen . Externe verwijzingen zijn beperkt tot één lid van de AGGREGATE, aangeduid als de root. Een reeks consistentieregels is van toepassing binnen de grenzen van AGGREGATE.
Het doel is om de invarianten te behouden. Maar het is ook bedoeld om de lokale identiteit correct te beheren, dwz de identificatie van objecten die niet alleen een betekenis hebben.
Order
en Order line
behoren definitief tot zon cluster. Bijvoorbeeld:
- Als u een
Order
verwijdert, moeten alle regels worden verwijderd. - Voor het verwijderen van een regel moeten de volgende regels mogelijk opnieuw worden genummerd.
- Voor het toevoegen van een nieuwe regel moet de regel nulber worden bepaald op basis van alle andere regels van dezelfde volgorde.
- Het wijzigen van sommige orderinformatie, zoals bijvoorbeeld de valuta, kan de betekenis van de prijs in de regelitems beïnvloeden (of vereisen dat de prijzen opnieuw worden berekend).
Dus hier het volledige totaal is vereist om consistente regels en invarianten te garanderen.
Wanneer moet u stoppen?
Nu beschrijft u enkele bedrijfsregels en stelt u dat u om deze te waarborgen, rekening moet houden met de klant als onderdeel van het totaal:
We hebben een bedrijf s regel die zegt dat een klant slechts één niet-bezorgde bestelling tegelijk kan hebben.
Natuurlijk, waarom niet. Laten we eens kijken naar de implicaties: de bestelling is altijd toegankelijk via de klant. Is dit echt? Wanneer werknemers de dozen vullen om de bestelling te bezorgen, moeten ze de streepjescode van de klant en de streepjescode van de bestelling lezen om toegang te krijgen tot de bestelling In feite is de identiteit van een Order in het algemeen globaal en niet lokaal voor een klant, en deze relatieve onafhankelijkheid suggereert om hem buiten het aggregaat te houden.
Bovendien zien deze bedrijfsregels er meer uit als beleid: het is een willekeurige beslissing van het bedrijf om hun proces met deze regels te laten verlopen. Als de regels niet worden gerespecteerd, is de baas misschien ongelukkig, maar de gegevens zijn niet echt inconsistent. En bovendien kan van de ene op de andere dag ” per klant één niet-bezorgde bestelling tegelijk ” worden ” tien niet-afgeleverde bestellingen per klant ” of zelfs ” onafhankelijk van de klant, honderd niet-afgeleverde bestellingen per magazijn “, zodat het aggregaat misschien niet langer gerechtvaardigd is.
Antwoord
Korte versie
De grondgedachte voor DDD is dat domeinobjecten abstracties zijn die zouden moeten voldoen aan uw functionele domeineisen – als domeinobjecten niet gemakkelijk aan die vereisten kunnen voldoen, suggereert dit dat u mogelijk de verkeerde abstractie gebruikt.
Naamgeving Domeinobjecten die Entiteitsnaamwoorden gebruiken, kunnen ertoe leiden dat die objecten nauw met elkaar worden gekoppeld en opgeblazen god-objecten worden, en ze kunnen problemen oproepen zoals die in deze vraag, zoals als “Waar is de juiste plek om p ut de CreateOrder-methode? “.
Om het gemakkelijker te maken om de “juiste” Aggregate Root te identificeren, kunt u een andere benadering overwegen waarbij Domain Objects zijn gebaseerd op de functionele zakelijke vereisten op hoog niveau, d.w.z.kies zelfstandige naamwoorden die verwijzen naar functionele vereisten en / of gedragingen die gebruikers van het systeem moeten uitvoeren.
Lange versie
DDD is een benadering van OO Design die bedoeld is om te resulteren in een grafiek van Domain Objects in de Business Laag van uw systeem – Domeinobjecten zijn verantwoordelijk voor het voldoen aan uw zakelijke vereisten op hoog niveau, en zouden idealiter moeten kunnen vertrouwen op de gegevenslaag voor zaken als de prestaties en integriteit van de onderliggende permanente gegevensopslag.
Een andere manier om ernaar te kijken zouden de opsommingstekens in deze lijst kunnen zijn.
- Entiteitsnaamwoorden suggereren doorgaans gegevensattributen.
- Domeinnaamwoorden zouden gedrag moeten suggereren
- DDD- en OO-modellering houden zich bezig met abstracties op basis van functionele vereisten en kerndomein / bedrijfslogica.
- De Business Logic Layer is verantwoordelijk voor het voldoen aan domeinvereisten op hoog niveau.
Een van de meest voorkomende misvattingen over DDD is dat domeinobjecten gebaseerd moeten zijn op een fysieke realiteit. wereld “ding” (dat wil zeggen een zelfstandig naamwoord waarnaar u in de echte wereld kunt verwijzen, toegeschreven aan allerlei gegevens / eigenschappen), maar de gegevens / attributen van die dingen uit de echte wereld vormen niet noodzakelijk een goed uitgangspunt wanneer u probeert functionele vereisten neer.
Natuurlijk moet Business Logic deze gegevens gebruiken , maar Domain Objects zelf zouden uiteindelijk abstracties moeten zijn die functionele domeinvereisten en gedragingen vertegenwoordigen.
Bijvoorbeeld; zelfstandige naamwoorden zoals Order
of Customer
impliceren geen enkel gedrag en zijn daarom over het algemeen niet bruikbare abstracties voor het weergeven van bedrijfslogica en domeinobjecten.
Wanneer u zoekt naar de soorten abstracties die nuttig kunnen zijn om Business Logic weer te geven, houd dan rekening met de typische vereisten waaraan u zou kunnen verwachten dat een systeem zou voldoen:
- Als verkoper moet ik wil een bestelling aanmaken voor een nieuwe klant, zodat ik een factuur kan genereren voor de te verkopen producten met hun prijzen en hoeveelheid.
- Als klantenserviceadviseur wil ik een lopende bestelling annuleren, zodat de Bestelling wordt niet uitgevoerd door een magazijnbeheerder.
- Als klantenserviceadviseur wil ik een orderregel retourneren zodat het product kan worden aangepast aan de inventaris en de betaling wordt terugbetaald via de oorspronkelijke betaling van de klant methode.
- Als magazijnbeheerder wil ik alle producten op een lopende bestelling en de verzendinformatie bekijken, zodat ik de producten kan kiezen en ze via de koerier kan verzenden.
- enz.
Domeinvereisten modelleren met een DDD-benadering
Overweeg op basis van de bovenstaande lijst een aantal mogelijke domeinobjecten ts voor zon Orders-systeem:
SalesOrderCheckout PendingOrdersStream WarehouseOrderDespatcher OrderRefundProcessor
Als domeinobjecten vertegenwoordigen deze abstracties die eigenaar worden van verschillende gedragsdomeinvereisten; inderdaad verwijzen hun zelfstandige naamwoorden sterk naar de specifieke functionele vereiste (n) waaraan ze voldoen.
(Er kan ook aanvullende infrastructuur zijn, zoals een EventMediator
om te slagen meldingen voor waarnemers die willen weten wanneer een nieuwe bestelling is gemaakt, of wanneer een bestelling is verzonden, enz.).
SalesOrderCheckout
moet bijvoorbeeld waarschijnlijk gegevens over klanten, verzending en producten verwerken, maar heeft niets te maken met het gedrag van bestellingen, het sorteren van lopende bestellingen of het geven van restituties.
Voor SalesOrderCheckout
om aan zijn domeineisen te voldoen, omvat het handhaven van die bedrijfsregels, zoals voorkomen dat klanten te veel items bestellen, mogelijk enige validatie uitvoeren en misschien meldingen voor andere delen van het systeem genereren – het kan al deze dingen doen zonder noodzakelijkerwijs afhankelijk te zijn van een van de andere objecten.
DDD gebruikt entiteitsnaamwoorden om domeinobjecten weer te geven
Ther Er zijn een aantal mogelijke gevaren bij het behandelen van eenvoudige zelfstandige naamwoorden zoals Order
, Customer
en Product
als domeinobjecten; onder die problemen zijn de problemen waarnaar u verwijst in de vraag:
- Als een methode een
Order
afhandelt, eenCustomer
en eenProduct
, tot welk domeinobject behoort het? - Waar is de geaggregeerde root voor die 3 objecten?
Als u Entiteitsnaam kiest om domeinobjecten weer te geven, kunnen er een aantal dingen gebeuren:
-
Order
,Customer
enProduct
lopen het risico om uit te groeien tot” god “-objecten - Risico om te eindigen met een enkele
Manager
god-object om alles samen te binden. - Die objecten lopen het risico nauw met elkaar verbonden te raken – het kan moeilijk zijn om aan domeineisen te voldoen zonder
this
(ofself
) - Een risico op het ontwikkelen van “lekkende” abstracties – dwzdomeinobjecten waarvan wordt verwacht dat ze tientallen
get
/set
-methoden weergeven die de inkapseling verzwakken (of, als u dat niet doet, dan kan een andere programmeur waarschijnlijk later ..). - Het risico bestaat dat domeinobjecten opgeblazen raken door een complexe mix van bedrijfsgegevens (bijv. invoer van gebruikersgegevens via een gebruikersinterface) en tijdelijke status (bijv. een geschiedenis van gebruikersacties wanneer de volgorde is gewijzigd).
DDD, OO Design en Plain Models
Een veel voorkomende misvatting over DDD en OO Design is dat “gewone” modellen op de een of andere manier ” bad of een antipatroon . Martin Fowler schreef een artikel waarin hij het Anemic Domain Model beschrijft – maar zoals hij in het artikel duidelijk maakt, moet DDD zelf de benadering van zuivere scheiding tussen lagen niet “tegenspreken”.
“Het is ook de moeite waard om te benadrukken dat het plaatsen van gedrag in de domeinobjecten niet in tegenspraak mag zijn met de solide benadering van het gebruik van gelaagdheid tot sepa beoordeel domeinlogica op basis van zaken als persistentie en presentatieverantwoordelijkheden. De logica die in een domeinobject zou moeten zitten, is domeinlogica – validaties, berekeningen, bedrijfsregels – hoe je het ook wilt noemen. “
Met andere woorden, het gebruik van eenvoudige modellen voor het bewaren van bedrijfsgegevens die tussen andere lagen worden overgedragen (bijv. Een bestelmodel dat wordt doorgegeven door een gebruikerstoepassing wanneer de gebruiker een nieuwe bestelling wil aanmaken) is niet hetzelfde als een “anemisch domeinmodel”. gewone gegevensmodellen zijn vaak de beste manier om gegevens bij te houden en gegevens over te dragen tussen lagen (zoals een REST-webservice, een persistentiestore, een applicatie of gebruikersinterface, enz.).
Bedrijfslogica kan de gegevens in die modellen en kan ze volgen als onderdeel van de bedrijfsstatus, maar “zal niet noodzakelijkerwijs eigenaar worden van die modellen.
The Aggregate Root
Nog eens kijken naar de voorbeelddomeinobjecten – SalesOrderCheckout
, PendingOrdersStream
, WarehouseOrderDespatcher
, OrderRefundProcessor
er is nog steeds geen duidelijke Aggregate Root; maar dat wel Het maakt eigenlijk niet uit, want deze domeinobjecten hebben enorm gescheiden verantwoordelijkheden die elkaar niet lijken te overlappen.
Functioneel gezien is het niet nodig dat de SalesOrderCheckout
met de PendingOrdersStream
praat omdat de taak van de eerste is voltooid wanneer het een nieuwe bestelling aan de database heeft toegevoegd; aan de andere kant kan de PendingOrdersStream
nieuwe bestellingen uit de database ophalen. Deze objecten hoeven niet echt met elk andere direct (misschien geeft een Event Mediator meldingen tussen de twee, maar ik verwacht dat elke koppeling tussen deze objecten erg los is)
Misschien is de Aggregate Root een IoC-container die een of meer van die domeinobjecten in een UI-controller, die ook andere infrastructuur levert zoals EventMediator
en Repository
. Of misschien is het een soort lichtgewicht Orchestrator Service die bovenop de Business Layer zit.
De samengevoegde root hoeft niet per se een domeinobject te zijn. Om de scheiding van zorgen tussen domeinobjecten te behouden, is het over het algemeen een goede zaak wanneer het root is een afzonderlijk object zonder zakelijke logica.
Reacties
- Ik heb downstemd omdat je antwoord concepten uit Entity Framework, een Microsoft-specifieke technologie, samenvoegt met Domain Driven Design, dat uit een boek komt geschreven door Eric Evans met dezelfde naam. Je hebt een aantal uitspraken in je antwoord die rechtstreeks in tegenspraak zijn met het DDD-boek en deze vraag maakt geen melding van Entity Framework, maar is specifiek getagd met DDD. Er ‘ s ook helemaal geen melding van persistentie in de vraag, dus ik ‘ zie niet waar databasetabellen relevant zijn.
- @RibaldEddie Bedankt dat je de tijd hebt genomen om het antwoord en de opmerking te bekijken, ik ben het ermee eens dat de vermelding van persistente gegevens niet ‘ t echt in het antwoord hoeft te staan, dus ik ‘ heb het opnieuw geformuleerd om dat te verwijderen. De belangrijkste focus van het antwoord zou kunnen worden samengevat als ” Entiteitsnaamwoorden zijn vaak ‘ t erg goede Domain Object-klassenamen vanwege hun neiging tot nauw gekoppelde opgeblazen god-objecten worden “, hopelijk is de boodschap en redenering WRT functionele vereisten / gedrag nu duidelijker?
- Het DDD-boek heeft ‘ hebben dat concept IIRC niet. Het heeft entiteiten, die slechts klassen zijn die, wanneer ze worden geïnstantieerd, een blijvende en unieke identiteit hebben, zodat twee afzonderlijke instanties twee unieke en blijvende zaken impliceren, wat in contrast staat met waardeobjecten die niet ‘ Ik heb geen identiteit en ‘ blijft niet bestaan in de tijd. In het boek zijn zowel Entities als Value-objecten domeinobjecten.
Antwoord
in ons probleemdomein hebben we een bedrijfsregel die zegt dat een klant slechts één niet-bezorgde bestelling tegelijk kan hebben.
Voordat u te diep in dat konijnenhol gaat, moet u Greg Young “s bespreking van ingestelde consistentie , en in het bijzonder:
Wat is de zakelijke impact van een storing?
Omdat in veel gevallen het juiste antwoord niet is om te proberen iets verkeerds gebeurt, maar in plaats daarvan uitzonderingsrapporten genereren wanneer er een probleem is.
Maar, ervan uitgaande dat er meerdere niet-afgeleverde bestellingen zijn een aanzienlijke aansprakelijkheid voor uw bedrijf …
Ja, als u er zeker van wilt zijn dat er slechts één niet-bezorgde bestelling is, dan moet er een aggregaat zijn dat alle bestellingen voor een klant kan zien .
Dat aggregaat is niet noodzakelijk het klantaggregaat .
Het kan zoiets zijn als een orderwachtrij of een orderhistorie, waarbij alle orders voor een specifieke klant gaat in dezelfde wachtrij. Van wat je hebt gezegd, het heeft niet alle profielgegevens van de klant nodig, dus dat zou geen deel moeten uitmaken van dit totaal.
Maar als het op verzending aankomt, voeren ze al hun acties uit op de bestelling, niet op de klant.
Ja, wanneer u daadwerkelijk werkt aan uitvoering en pull sheets, de geschiedenisweergave is niet bijzonder relevant.
De geschiedenisweergave, om uw invariant af te dwingen, heeft alleen het order-ID en de huidige verwerkingsstatus nodig. Dat hoeft niet per se deel uit te maken van hetzelfde aggregaat als de order – onthoud dat geaggregeerde grenzen gaan over het beheren van veranderingen, niet over het structureren van views.
Het kan dus zijn dat je de order als een aggregaat behandelt , en de bestelgeschiedenis als een afzonderlijk aggregaat, en coördineer de activiteit tussen de twee.
Answer
Je hebt een stromend voorbeeld. Het is te simplistisch en ik betwijfel of het een realistisch systeem weerspiegelt. Ik zou die entiteiten en hun gerelateerde gedrag daarom niet modelleren op de manier die je hebt gespecificeerd.
Je klassen moeten de staat van een order op een manier die wordt weerspiegeld in meerdere aggregaten. Als de klant het systeem bijvoorbeeld in de staat zet waarin het bestelverzoek van de klant moet worden verwerkt, kan ik een aggregaat van een domeinentiteitsobject maken met de naam CustomerOrderRequest
of PendingCustomerOrder
of zelfs gewoon CustomerOrder
, of welke taal het bedrijf ook gebruikt, en het zou een aanwijzer kunnen zijn naar zowel de klant als de OrderLines en dan een methode hebben zoals canCustomerCompleteOrder()
die wordt aangeroepen vanuit de servicelaag.
Dit domeinobject zou de bedrijfslogica bevatten om te bepalen of de bestelling geldig was.
Als de bestelling geldig en verwerkt was, zou ik “een manier hebben om dit object over te zetten naar een ander object dat de verwerkte bestelling vertegenwoordigde.
Ik denk dat het probleem met uw begrip is dat u een gekunstelde een te vereenvoudigd voorbeeld van aggregaten. Een PendingOrder
kan het “eigen aggregaat zijn, gescheiden van een UndeliveredOrder
en weer gescheiden van een of een CancelledOrder
of wat dan ook.
Reacties
- Hoewel je poging dat genderneutraal taalgebruik grappig is, zou ik willen opmerken dat vrouwen nooit in het veld staan om kraaien af te schrikken.
- @RobertHarvey dat ‘ iets vreemds is om op te focussen in mijn post. Vogelverschrikkers en beeltenissen zijn in de loop van de geschiedenis beide regelmatig in vrouwelijke vorm verschenen.
- Je zou ‘ niet het onderscheid hebben gemaakt in je bericht als je ‘ vinden het niet belangrijk. In taalkundig opzicht is de term ” stroman; ” eventuele bedenkingen over seksisme worden vrijwel zeker overtroffen door de ” waar heeft hij het in vredesnaam over ” factor gecreëerd door je eigen term te verzinnen.
- @RobertHarvey als iemand weet wat een rietje man bedoelt, ik ‘ m zeker dat ze kunnen achterhalen wat een stroman bedoelt als ze ‘ die term niet hebben gehoord. Kunnen we ons concentreren op de inhoud van mijn bericht, alstublieft mbt software?
Antwoord
Vaughn Vernon vermeldt dit in zijn boek “Implementing Domain-Driven Design” aan het begin van Hoofdstuk 7 (Services):
“Vaak is de beste indicatie dat u een Service in het domeinmodel moet maken, wanneer de bewerking die u moet uitvoeren, aanvoelt van plaats als een methode op een aggregaat of een waardeobject “.
In dit geval kan er dus een domeinservice zijn genaamd “CreateOrderService” die een exemplaar van de klant en de lijst met items voor de bestelling gebruikt.
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 } }
Opmerkingen
- Kunt u alstublieft meer uitleggen hoe domeinservice kan helpen bij het oplossen van problemen met gelijktijdigheid in de vraag?
Geef een reactie