Finn DDD Aggregate Root
On februar 1, 2021 by adminLa oss spille alles favorittspill, finn Aggregrate Root. La oss bruke det kanoniske domenet Customer / Order / OrderLines / Product problem. Tradisjonelt er Customer, order og product AR-ene med OrderLines som enheter under Order. Logikken bak dette er at du trenger å identifisere kunder, ordrer og produkter, men en OrderLine ville ikke eksistere uten en ordre. Så i vårt problemdomene har vi en forretningsregel som sier at en kunde bare kan ha en ikke-levert ordre av gangen.
Flytter det bestillingen under kundens samlede rot? Jeg tror det gjør det. Men på den måten gjør det kunden AR ganske stor og utsatt for problemer med samtidighet senere.
Eller hva om vi hadde en forretningsregel som sier at en kunde bare kan bestille et bestemt produkt en gang i løpet av livet. Dette er mer bevis som krever at kunden eier ordren.
Men når det kommer til forsendelse, gjør de alle sine handlinger på bestillingen, ikke kunden. Det er litt dumt å måtte laste opp hele kunden for å merke en individuell bestilling som levert.
Dette er hva jeg foreslår:
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 } }
Min største bekymring er samsvarsproblemet og det faktum at selve ordenen har karakteristiske s av en samlet rot.
Svar
Hva er kriteriene for å definere et aggregat?
La oss gå tilbake til det grunnleggende i den store blå boken:
Aggregat: En klynge med tilknyttede objekter som behandles som en enhet for dataendringer . Eksterne referanser er begrenset til ett medlem av AGGREGATE, utpekt som roten. Et sett med konsistensregler gjelder innenfor AGGREGATES grenser.
Målet er å opprettholde invarianter. Men det er også å håndtere riktig lokal identitet, dvs. identifisere objekter som ikke har en mening alene.
Order
og Order line
tilhører definitivt en slik klynge. For eksempel:
- Slett en
Order
, vil kreve sletting av alle linjene. - Hvis du sletter en linje, kan det kreve at nummeret på følgende linjer blir nummerert på nytt
- Hvis du vil legge til en ny linje, må linjestoppen bestemmes ut fra alle de andre linjene i samme rekkefølge.
- Endring av ordreinformasjon, for eksempel valutaen, kan påvirke betydningen av prisen i ordrelinjene (eller kreve å beregne prisene på nytt).
Så her er det samlede aggregatet kreves for å sikre konsistensregler og invarianter.
Når skal du stoppe?
Nå beskriver du noen forretningsregler, og hevder at for å sikre dem, trenger du å ta hensyn til kunden som en del av aggregatet:
Vi har en virksomhet s regel om at en kunde bare kan ha en ikke-levert ordre om gangen.
Selvfølgelig, hvorfor ikke. La oss se implikasjonene: Bestillingen vil alltid være tilgjengelig via kunden. Er dette det virkelige livet? Når arbeidere fyller boksene for å levere ordren, må de lese kundens strekkode og strekkoden for å få tilgang til bestillingen. Faktisk generelt er identiteten til en ordre global, ikke lokal for en kunde, og denne relative uavhengigheten antyder å holde ham utenfor aggregatet.
I tillegg ser disse forretningsreglene mer ut som retningslinjer: det er en vilkårlig beslutning fra selskapet å kjøre prosessen med disse reglene. Hvis reglene ikke blir respektert, kan sjefen være misfornøyd, men dataene er egentlig ikke inkonsekvente. Og dessuten kan » over hver dag en ikke-levert ordre om gangen » bli » ti ikke-leverte ordrer per kunde » eller til og med » uavhengig av kunden, hundre ikke-leverte ordrer per lager «, slik at aggregatet ikke lenger kan rettferdiggjøres.
Svar
Kortversjon
Begrunnelsen for DDD er at Domain Objects er abstraksjoner som skal oppfylle dine funksjonelle domenekrav. Hvis Domain Objects ikke klarer å oppfylle disse kravene, antyder det at du bruker feil abstraksjon.
Navngivning Domeneobjekter som bruker Entitetsnavn kan føre til at disse objektene blir tett koblet med hverandre og blir oppblåste «gud» -objekter, og de kan kaste opp problemer som den i dette spørsmålet som «Hvor er rett sted å s ut CreateOrder-metoden? «.
For å gjøre det lettere å identifisere den «riktige» Aggregate Root, bør du vurdere en annen tilnærming der Domain Objects er basert på de funksjonelle kravene på høyt nivå – dvs.velge substantiver som viser til funksjonelle krav og / eller atferd som brukere av systemet trenger å utføre.
Lang versjon
DDD er en tilnærming til OO Design som er ment å resultere i en graf av Domain Objects i Business Lag av systemet ditt – Domeneobjekter er ansvarlige for å tilfredsstille dine høye forretningskrav, og bør ideelt sett være i stand til å stole på datalaget for ting som ytelsen og integriteten til den underliggende vedvarende datalageret.
En annen måte å se på det kan være kulepunktene i denne listen
- Enhetsbetegnelser foreslår vanligvis dataattributter.
- Domenenavn bør foreslå atferd
- DDD og OO Modellering er opptatt av abstraksjoner basert på funksjonelle krav og kjernedomen / forretningslogikk.
- Business Logic Layer er ansvarlig for å tilfredsstille domenekrav på høyt nivå
En av de vanligste misforståelsene angående DDD er at Domain Objects skal være basert på noen fysisk verdens «ting» (dvs. noe substantiv som du kan peke på i den virkelige verden, tilskrevet med alle slags data / egenskaper), men dataene / attributtene til de virkelige tingene gir ikke nødvendigvis et godt utgangspunkt når du prøver å spikre ned funksjonelle krav.
Selvfølgelig bør Business Logic bruke disse dataene, men Domain Objects i seg selv skal til slutt være abstraksjoner som representerer funksjonelle domenekrav og atferd.
For eksempel; substantiver som Order
eller Customer
innebærer ingen oppførsel, og er derfor generelt lite nyttige abstraksjoner for å representere forretningslogikk og domeneobjekter.
Når du leter etter hva slags abstraksjoner som kan være nyttige for å representere Business Logic, bør du vurdere typiske krav som du kan forvente at et system skal oppfylle:
- Som selger, jeg ønsker å opprette en ordre for en ny kunde, slik at jeg kan generere en faktura for produktene som skal selges med deres priser og mengde.
- Som kundeservicerådgiver vil jeg kansellere en ventende ordre slik at Bestillingen blir ikke oppfylt av en lageroperatør.
- Som kundeservicerådgiver vil jeg returnere en ordrelinje slik at produktet kan justeres i varelageret og betaling refunderes via kundens originale betaling. metode.
- Som lageroperatør vil jeg se alle produktene på en ventende ordre og forsendelsesinformasjonen slik at jeg kan plukke produktene og sende dem via kureren.
- etc.
Krav til modellering av domener med en DDD-tilnærming
Basert på listen ovenfor, vurder noen potensielle domeneobjekter ts for et slikt ordresystem:
SalesOrderCheckout PendingOrdersStream WarehouseOrderDespatcher OrderRefundProcessor
Som domeneobjekter representerer disse abstraksjoner som tar eierskap til ulike atferdsmessige domenekrav; Faktisk antyder substantivene deres sterkt på de spesifikke funksjonskravene de oppfyller.
(Det kan være ekstra infrastruktur der også, for eksempel et EventMediator
å passere varsler for observatører som ønsker å vite når en ny ordre er opprettet, eller når en ordre er sendt osv.).
For eksempel trenger SalesOrderCheckout
sannsynligvis håndtere data om kunder, frakt og produkter, men er ikke opptatt av noe å gjøre med oppførselen for fraktordre, sortering av ventende ordrer eller utstedelse av refusjoner.
For SalesOrderCheckout
for å oppfylle domenekravene inkluderer håndhevelse av forretningsreglene, som å forhindre at kunder bestiller for mange varer, muligens kjører noen validering, og kanskje hevder varsler for andre deler av systemet – det kan gjøre alle disse tingene uten nødvendigvis å måtte stole på noen av de andre objektene.
DDD bruker Entity Nouns til å representere domeneobjekter
Ther e er en rekke potensielle farer ved behandling av enkle substantiver som Order
, Customer
og Product
som domeneobjekter; blant disse problemene er de du henviser til i spørsmålet:
- Hvis en metode håndterer en
Order
, enCustomer
og etProduct
, hvilket domeneobjekt tilhører det? - Hvor er den samlede roten for de tre objektene?
Hvis du velger Enhetsnavn for å representere domeneobjekter, kan det skje en rekke ting:
-
Order
,Customer
ogProduct
risikerer å vokse til» gud «-objekter - Risiko for å ende opp med en enkelt
Manager
gud-objekt for å knytte alt sammen. - Disse objektene risikerer å bli tett koblet til hverandre – det kan være vanskelig å oppfylle domenekrav uten å passere
this
(ellerself
) - En risiko for å utvikle «lekk» abstraksjoner – dvs.Det forventes at domeneobjekter eksponerer dusinvis av
get
/set
metoder som svekker innkapsling (eller, hvis du ikke gjør det, så en annen programmerer sannsynligvis senere ..). - En risiko for at domeneobjekter blir oppblåste med en kompleks blanding av forretningsdata (f.eks. brukerdatainngang via et brukergrensesnitt) og forbigående tilstand (f.eks. en «historie» om brukerhandlinger når ordren er endret).
DDD, OO Design og Plain Models
En vanlig misforståelse angående DDD og OO Design er at «vanlige» modeller på en eller annen måte er dårlig «eller et» antimønster «. Martin Fowler skrev en artikkel som beskriver Anemic Domain Model – men som han gjør det klart i artikkelen, bør DDD selv ikke «motsier» tilnærmingen til ren skille mellom lag
«Det er også verdt å understreke at å sette oppførsel i domenegjenstandene ikke skal motsette seg den solide tilnærmingen til å bruke lagdeling til sepa rangere domenelogikk fra slike ting som utholdenhets- og presentasjonsansvar. Logikken som skal være i et domeneobjekt er domenelogikk – valideringer, beregninger, forretningsregler – hva du vil kalle det. «
Med andre ord, å bruke vanlige modeller for å holde forretningsdata overført mellom andre lag (f.eks. En ordremodell sendt inn av et brukerprogram når brukeren ønsker å opprette en ny ordre) er ikke det samme som en «Anemic Domain Model». «vanlige» datamodeller er ofte den beste måten å spore data og overføre data mellom lag (for eksempel en REST-webtjeneste, en persistensbutikk, et program eller et brukergrensesnitt osv.).
Forretningslogikk kan behandle data i disse modellene og kan spore dem som en del av forretningsstaten – men vil ikke nødvendigvis ta eierskap til disse modellene.
Den samlede roten
Ser igjen på eksemplet Domain Objects – SalesOrderCheckout
, PendingOrdersStream
, WarehouseOrderDespatcher
, OrderRefundProcessor
det er fremdeles ingen åpenbar samlet rot, men det gjør det det har ikke noe å si fordi disse domeneobjektene har et veldig eget ansvar som ikke ser ut til å overlappe hverandre.
Funksjonelt er det ikke behov for SalesOrderCheckout
for å snakke med PendingOrdersStream
fordi jobben til den tidligere er fullført når den har lagt til en ny ordre i databasen. På den annen side kan PendingOrdersStream
hente nye ordrer fra databasen. Disse objektene trenger faktisk ikke å samhandle med hver andre direkte (Kanskje en hendelsesformidler kan gi varsler mellom de to, men jeg forventer at enhver kobling mellom disse objektene er veldig løs)
Kanskje vil aggregerte rot være en IoC-beholder som injiserer en eller flere av disse domeneobjektene til en brukergrensesnittkontroller, som også leverer annen infrastruktur som EventMediator
og Repository
. Eller kanskje det vil være en slags lett Orchestrator Service som sitter på toppen av Business Layer.
Den samlede roten trenger ikke nødvendigvis å være et domeneobjekt. For å opprettholde skillet mellom bekymringer mellom domeneobjekter, er det vanligvis en god ting når det samlede root er et eget objekt uten forretningslogikk.
Kommentarer
- Jeg nedstemte fordi svaret ditt samler konsepter fra Entity Framework, som er en Microsoft-spesifikk teknologi med Domain Driven Design, som er fra en bok skrevet av Eric Evans med samme navn. Du har noen uttalelser i svaret ditt som er i direkte motstrid med DDD-boken, og dette spørsmålet nevner null Entity Framework, men er spesifikt merket med DDD. Der ‘ er det heller ingen omtale av utholdenhet i spørsmålet, så jeg ser ikke ‘ hvor databasetabeller er relevante.
- @RibaldEddie Takk for at du tok deg tid til å gå gjennom svaret og kommentere, jeg er enig i at nevnelsen av vedvarende data ikke ‘ ikke trenger å være i svaret, så jeg ‘ har omformulert det for å fjerne det. Hovedfokuset i svaret kan oppsummeres som » Enhetsnavn er ofte ikke ‘ t veldig gode navn på domeneobjektklasser på grunn av deres tendens til bli tett sammenkoblede oppblåste gudobjekter «, forhåpentligvis er meldingen og resonnementet WRT funksjonelle krav / oppførsel klarere nå?
- DDD-boken ‘ t har det konseptet IIRC. Den har enheter, som bare er klasser som når de blir instansert har en vedvarende og unik identitet, slik at to separate eksempler innebærer to unike og vedvarende ting, som står i kontrast til verdiobjekter som ikke ‘ t har noen identitet og ikke ‘ t vedvarer over tid. I boka er både Entities and Value objects domeneobjekter.
Svar
i vårt problemdomene har vi en forretningsregel som sier at en kunde bare kan ha en ikke-levert ordre om gangen.
Før du går for dypt ned i kaninhullet, bør du gå gjennom Greg Youngs diskusjon om angir konsistens , og spesielt:
Hva er virksomhetseffekten av å ha en fiasko?
Fordi i mange tilfeller er det riktige svaret ikke for å prøve å forhindre feil ting skjer, men i stedet for å generere unntaksrapporter når det kan være et problem.
Men forutsatt at flere ikke-leverte ordrer er et betydelig ansvar overfor virksomheten din ….
Ja, hvis du vil være sikker på at det bare er en ikke-levert ordre, må det være noe aggregat som kan se alle ordrene til en kunde .
Det aggregatet er ikke nødvendigvis kundeaggregatet .
Det kan være omtrent en ordrekø eller en ordrehistorikk der alle ordrene for en bestemt kunden går i samme kø. Fra det du har sagt, trenger det ikke alle kundens profildata, slik at det ikke skal være en del av dette aggregatet.
Men når det gjelder frakt, gjør de alle sine handlinger på bestillingen, ikke kunden.
Ja, når du faktisk jobber med oppfyllelse og trekk ark, historikkvisningen er ikke spesielt relevant.
Historikkvisningen, for å håndheve din invariant, trenger bare ordre-ID og den nåværende behandlingsstatusen. Det trenger ikke nødvendigvis å være en del av det samme aggregatet som bestillingen – husk at samlede grenser handler om å håndtere endring, ikke strukturere visninger.
Så det kan være at du håndterer ordren som et aggregat. , og ordrehistorikken som et eget aggregat, og koordinerer aktiviteten mellom de to.
Svar
Du har satt opp et stråpersoneksempel. Det er for forenklet, og jeg tviler på at det gjenspeiler et system fra den virkelige verden. Jeg ville ikke modellere enhetene og deres relaterte oppførsel på den måten du spesifiserte på grunn av det.
Klassene dine må modellere ordrenes tilstand på en måte som gjenspeiles i flere aggregater. For eksempel når kunden setter systemet i den tilstanden hvor kundens bestillingsforespørsel må behandles, kan jeg opprette et domeneenhetsobjektaggregat kalt CustomerOrderRequest
eller PendingCustomerOrder
eller til og med bare CustomerOrder
, eller hvilket språk som virksomheten bruker, og den kan holde en pekepinn til både kunden og ordrelinjene og deretter ha en metode som div id = «78fc665fbb»>
som kalles fra tjenestelaget.
Dette domeneobjektet vil inneholde forretningslogikken for å avgjøre om bestillingen var gyldig eller ikke.
Hvis bestillingen var gyldig og behandlet, ville jeg ha en måte å overføre dette objektet til et annet objekt som representerte den behandlede ordren.
Jeg tror problemet med din forståelse er at du bruker en konstruert overforenklet eksempel på aggregater. Et PendingOrder
kan være det eget aggregat atskilt fra et UndeliveredOrder
og igjen atskilt fra et eller en CancelledOrder
eller hva som helst.
Kommentarer
- Selv om du prøver på kjønnsnøytralt språk er morsomt, vil jeg merke at kvinner aldri står på åker for å skremme kråkene.
- @RobertHarvey at ‘ er en merkelig ting å fokusere på i innlegget mitt. Fugleskremsel og figurer begge har regelmessig dukket opp i kvinnelig form gjennom historien.
- Du ville ikke ‘ ikke ha gjort skillet i innlegget ditt hvis du ikke ‘ t anser det som viktig. Som et spørsmål om lingvistikk er begrepet » stråmann; » eventuelle forbehold om sexisme blir nesten helt sikkert trumfet av » hva i helvete snakker han om » faktor opprettet ved å oppfinne ditt eget begrep.
- @RobertHarvey hvis noen vet hva halm mannen betyr, jeg ‘ er sikker på at de kan finne ut hva halmpersonen mener hvis de ikke har ‘ ikke hørt det ordet. Kan vi fokusere på innholdet i innlegget mitt, vennligst skriv programvare?
Svar
Vaughn Vernon nevner dette i sin bok «Implementing Domain-Driven Design» i begynnelsen av kapittel 7 (Tjenester):
«Ofte er den beste indikasjonen på at du skal opprette en tjeneste i domenemodellen når operasjonen du trenger å utføre føles ute av sted som en metode på et samlet eller et verdiobjekt «.
Så i dette tilfellet kan det være en domenetjeneste kalt «CreateOrderService» som tar en kundeinstans og listen over varer for bestillingen.
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 } }
Kommentarer
- Kan du forklare mer hvordan domenetjeneste kan hjelpe deg med å løse samtidige bekymringer i spørsmålet?
Legg igjen en kommentar