Trova il DDD Aggregate Root
Su Febbraio 1, 2021 da adminGiochiamo al gioco preferito di tutti, trova Aggregrate Root. Usiamo il canonico dominio del problema Cliente / Ordine / OrderLines / Prodotto. Tradizionalmente, Cliente, ordine e prodotto sono gli AR con OrderLines come entità sotto lOrdine. La logica alla base di questo è che è necessario identificare clienti, ordini e prodotti, ma un OrderLine non esisterebbe senza un Ordine. Pertanto, nel nostro dominio problematico, abbiamo una regola aziendale che afferma che un cliente può avere solo un ordine non consegnato alla volta.
Questo sposta lordine sotto la radice aggregata del cliente? Penso di sì. Ma così facendo, lAR del cliente diventa piuttosto grande e soggetto a problemi di concorrenza in seguito.
Oppure, se avessimo una regola aziendale che afferma che un cliente può ordinare un determinato prodotto solo una volta nella sua vita. Questa è unulteriore prova che richiede al cliente di possedere lordine.
Ma quando arriva alla spedizione, fanno tutte le loro azioni sullOrdine, non sul cliente. È un po stupido dover caricare lintero cliente per contrassegnare un singolo Ordine come consegnato.
Questo è quello che sto proponendo:
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 } }
La mia più grande preoccupazione è il problema della concorrenza e il fatto che lOrdine stesso abbia caratteristiche s di una radice aggregata.
Risposta
Quali sono i criteri per definire un aggregato?
Torniamo alle basi del grande libro blu:
Aggregato: Un cluster di oggetti associati che vengono trattati come ununità ai fini delle modifiche ai dati . I riferimenti esterni sono limitati a un membro dellAGGREGATO, designato come radice. Una serie di regole di coerenza si applica allinterno dei confini di AGGREGATE.
Lobiettivo è mantenere gli invarianti. Ma serve anche a gestire adeguatamente lidentità locale, cioè lidentificazione di oggetti che non hanno un significato da soli.
Order
e Order line
appartiene definitivamente a tale cluster. Ad esempio:
- Elimina un
Order
, richiederà leliminazione di tutte le sue righe. - Leliminazione di una riga potrebbe richiedere la rinumerazione delle seguenti righe
- Laggiunta di una nuova riga richiederebbe la determinazione dellannullamento della riga in base a tutte le altre righe dello stesso ordine.
- La modifica di alcune informazioni sullordine, come ad esempio la valuta, potrebbe influire sul significato del prezzo negli elementi pubblicitari (o richiedere il ricalcolo dei prezzi).
Quindi qui laggregato completo è necessario per garantire la coerenza tra regole e invarianti.
Quando fermarsi?
Ora, descrivi alcune regole aziendali e affermi che per garantirle, dovresti considerare il cliente come parte dellaggregato:
Abbiamo un business regola di s che dice che un cliente può avere solo un ordine non consegnato alla volta.
Ovviamente, perché no. Vediamo le implicazioni: lordine sarà sempre accessibile tramite il cliente. È questa la vita reale? Quando i lavoratori riempiono le scatole per la consegna dellordine, dovranno leggere il codice a barre del cliente e il codice a barre dellordine per accedere allordine ? In effetti, in generale, lidentità di un ordine è globale, non locale per un cliente e questa relativa indipendenza suggerisce di tenerlo fuori dallaggregato.
Inoltre, queste regole aziendali sembrano più politiche: è una decisione arbitraria dellazienda di eseguire il processo con queste regole. Se le regole non vengono rispettate, il capo potrebbe essere scontento, ma i dati non sono realmente incoerenti. Inoltre, ” durante la notte per cliente un ordine non consegnato alla volta ” potrebbe diventare ” dieci ordini non consegnati per cliente ” o anche ” indipendentemente dal cliente, cento ordini non consegnati per magazzino “, in modo che laggregazione potrebbe non essere più giustificata.
Risposta
Versione breve
La logica per DDD è che gli oggetti di dominio sono astrazioni che dovrebbero soddisfare i requisiti del tuo dominio funzionale: se gli oggetti di dominio non sono in grado di soddisfare facilmente tali requisiti, significa che potresti utilizzare lastrazione sbagliata.
Denominazione Gli oggetti di dominio utilizzando nomi di entità possono far sì che quegli oggetti diventino strettamente accoppiati tra loro e diventino oggetti “divini” gonfiati e possono sollevare problemi come quello in questa domanda come come “Dovè il posto giusto per p ut il metodo CreateOrder? “.
Per rendere più facile identificare la radice aggregata “giusta”, considera un approccio diverso in cui gli oggetti di dominio si basano sui requisiti aziendali funzionali di alto livello, ad es.scegliere nomi che alludono a requisiti funzionali e / o comportamenti che gli utenti del sistema devono eseguire.
Versione lunga
DDD è un approccio alla progettazione OO che ha lo scopo di produrre un grafico di Oggetti di dominio nel Business Livello del sistema: gli oggetti di dominio sono responsabili della soddisfazione dei requisiti aziendali di alto livello e, idealmente, dovrebbero essere in grado di fare affidamento sul livello dati per cose come le prestazioni e lintegrità dellarchivio dati persistente sottostante.
Un altro modo per vederlo potrebbero essere i punti elenco in questo elenco
- I nomi di entità tipicamente suggeriscono attributi di dati.
- I nomi di dominio dovrebbero suggerire il comportamento
- DDD e OO Modeling si occupano di astrazioni basate su requisiti funzionali e logica di dominio / business principale.
- Il livello di logica aziendale è responsabile della soddisfazione dei requisiti di dominio di alto livello
Uno dei pregiudizi comuni riguardo a DDD è che gli oggetti di dominio dovrebbero essere basati su alcuni “cosa” del mondo (cioè un nome che puoi indicare nel mondo reale, attribuito con tutti i tipi di dati / proprietà), tuttavia i dati / attributi di quelle cose del mondo reale non sono necessariamente un buon punto di partenza quando cerchi di inchiodare requisiti funzionali bassi.
Ovviamente, la logica di business dovrebbe utilizzare questi dati, ma gli oggetti di dominio stessi dovrebbero in ultima analisi essere astrazioni che rappresentano i requisiti e i comportamenti del dominio funzionale.
Ad esempio; nomi come Order
o Customer
non implicano alcun comportamento e pertanto sono generalmente astrazioni inutili per rappresentare la logica aziendale e gli oggetti di dominio.
Quando cerchi i tipi di astrazioni che potrebbero essere utili per rappresentare la logica aziendale, considera i requisiti tipici che potresti aspettarti che un sistema soddisfi:
- In qualità di venditore, io desidera creare un ordine per un nuovo cliente in modo da poter generare una fattura per i prodotti da vendere con i relativi prezzi e quantità.
- In qualità di consulente del servizio clienti desidero annullare un ordine in sospeso in modo che il Lordine non viene evaso da un operatore di magazzino.
- In qualità di consulente del servizio clienti desidero restituire una riga ordine in modo che il prodotto possa essere regolato nellinventario e il pagamento venga rimborsato tramite il pagamento originale del cliente metodo.
- In qualità di Operatore di magazzino desidero visualizzare tutti i prodotti in un ordine in sospeso e le informazioni di spedizione in modo da poter ritirare i prodotti e spedirli tramite il corriere.
- ecc.
Modellazione dei requisiti di dominio con un approccio DDD
In base allelenco precedente, prendi in considerazione alcuni potenziali oggetti di dominio ts per un tale sistema di ordini:
SalesOrderCheckout PendingOrdersStream WarehouseOrderDespatcher OrderRefundProcessor
In quanto oggetti di dominio, questi rappresentano astrazioni che assumono la proprietà di vari requisiti di dominio comportamentali; in effetti i loro nomi suggeriscono fortemente i requisiti funzionali specifici che soddisfano.
(Potrebbe esserci anche uninfrastruttura aggiuntiva come un EventMediator
da superare notifiche per gli osservatori che desiderano sapere quando è stato creato un nuovo ordine, o quando è stato spedito un ordine, ecc.).
Ad esempio, SalesOrderCheckout
probabilmente deve gestire i dati su clienti, spedizione e prodotti, tuttavia non si occupa di nulla che abbia a che fare con il comportamento degli ordini di spedizione, lordinamento degli ordini in sospeso o lemissione di rimborsi.
Per SalesOrderCheckout
per soddisfare i suoi requisiti di dominio include lapplicazione di quelle regole aziendali come impedire ai clienti di ordinare troppi articoli, possibilmente eseguire qualche convalida e forse aumentare le notifiche per altre parti del sistema: può fare tutte queste cose senza necessariamente dover dipendere da qualsiasi altro oggetto.
DDD che utilizza nomi di entità per rappresentare oggetti di dominio
Ther Ci sono una serie di potenziali pericoli quando si trattano nomi semplici come Order
, Customer
e Product
come oggetti di dominio; tra questi problemi ci sono quelli a cui alludi nella domanda:
- Se un metodo gestisce un
Order
, unCustomer
e unProduct
, a quale oggetto di dominio appartiene? - Dovè la radice aggregata per questi 3 oggetti?
Se scegli nomi di entità per rappresentare oggetti di dominio, possono accadere diverse cose:
-
Order
,Customer
eProduct
rischiano di trasformarsi in oggetti” dio “ - Rischio di ritrovarsi con un unico
Manager
oggetto-dio per legare tutto insieme. - Questi oggetti rischiano di essere strettamente accoppiati luno con laltro: potrebbe essere difficile soddisfare i requisiti del dominio senza passare
this
(oself
) - Rischio di sviluppare astrazioni “leaky”, ad es.oggetti di dominio che dovrebbero esporre dozzine di
get
/set
metodi che indeboliscono lincapsulamento (o, se non lo fai, qualche altro programmatore probabilmente lo farò più tardi ..). - Rischio che gli oggetti di dominio si gonfino con una complessa combinazione di dati aziendali (ad esempio, input di dati utente tramite uninterfaccia utente) e stato transitorio (ad esempio una “cronologia” delle azioni dellutente quando lordine è stato modificato).
DDD, OO Design e Plain Models
Un malinteso comune riguardo a DDD e OO Design è che i modelli “semplici” siano in qualche modo ” bad “o un” anti-pattern “. Martin Fowler ha scritto un articolo descrivendo il Anemic Domain Model , ma come chiarisce nellarticolo, DDD stesso dovrebbe non “contraddire” lapproccio della netta separazione tra i livelli
“Vale anche la pena sottolineare che inserire un comportamento nel dominio degli oggetti non dovrebbe contraddire lapproccio solido di usare la stratificazione per sepa valutare la logica del dominio da cose come la persistenza e le responsabilità di presentazione. La logica che dovrebbe trovarsi in un oggetto di dominio è la logica di dominio: convalide, calcoli, regole aziendali, come preferisci chiamarlo. “
In altre parole, lutilizzo di modelli semplici per conservare i dati aziendali trasferiti tra altri livelli (ad esempio un modello di ordine passato da unapplicazione utente quando lutente desidera creare un nuovo ordine) non è la stessa cosa di un “modello di dominio anemico”. i modelli di dati “semplici” sono spesso il modo migliore per tenere traccia dei dati e trasferire i dati tra i livelli (come un servizio Web REST, un archivio di persistenza, unapplicazione o uninterfaccia utente, ecc.).
La logica aziendale può elaborare il dati in quei modelli e potrebbe tenerne traccia come parte dello stato aziendale, ma non sarà necessariamente il proprietario di quei modelli.
The Aggregate Root
Guardando di nuovo agli oggetti di dominio di esempio – SalesOrderCheckout
, PendingOrdersStream
, WarehouseOrderDespatcher
, OrderRefundProcessor
non esiste ancora una radice aggregata ovvia, ma è così In realtà non importa perché questi Domain Object hanno responsabilità completamente separate che non sembrano sovrapporsi.
Funzionalmente, non è necessario che SalesOrderCheckout
parli con PendingOrdersStream
perché il lavoro del primo è completo quando ha aggiunto un nuovo ordine al database; daltra parte, PendingOrdersStream
può recuperare nuovi ordini dal database. Questi oggetti non hanno bisogno di interagire con ciascuno altro direttamente (forse un Mediatore di eventi potrebbe fornire notifiche tra i due, ma mi aspetto che qualsiasi accoppiamento tra questi oggetti sia molto lento)
Forse la radice aggregata sarà un contenitore IoC che inietta uno o più di quegli oggetti di dominio in un controller dellinterfaccia utente, fornendo anche altre infrastrutture come EventMediator
e Repository
. O forse sarà una sorta di servizio orchestrator leggero posto in cima al livello aziendale.
La radice aggregata non deve essere necessariamente un oggetto dominio. Per mantenere la separazione delle preoccupazioni tra oggetti dominio, è generalmente una buona cosa quando laggregazione root è un oggetto separato senza logica di business.
Commenti
- Ho ricevuto un voto negativo perché la tua risposta fonde concetti di Entity Framework, una tecnologia specifica di Microsoft, con Domain Driven Design, che è tratto da un libro scritto da Eric Evans con lo stesso nome. Hai alcune affermazioni nella tua risposta che sono in diretta contraddizione con il libro DDD e questa domanda non fa alcuna menzione di Entity Framework ma specificamente è contrassegnata con DDD. Inoltre ‘ non si fa menzione della persistenza nella domanda, quindi non ‘ per vedere dove sono rilevanti le tabelle del database.
- @RibaldEddie Grazie per aver dedicato del tempo per esaminare la risposta e il commento, sono daccordo che la menzione di dati persistenti non ‘ abbia davvero bisogno di essere nella risposta, quindi ‘ lho riformulato per rimuoverlo. Lobiettivo principale della risposta potrebbe essere riassunto come ” I nomi delle entità spesso non sono ‘ t nomi di classi di oggetti di dominio molto buoni a causa della loro tendenza a diventare oggetti divini gonfiati strettamente accoppiati “, si spera che il messaggio e il ragionamento dei requisiti funzionali / comportamento WRT siano più chiari ora?
- Il libro DDD non ‘ t avere quel concetto IIRC. Ha entità, che sono semplicemente classi che quando istanziate hanno unidentità persistente e unica in modo che due istanze separate implicano due cose uniche e persistenti, che contrasta con oggetti valore che don ‘ Non avere alcuna identità e non ‘ t persistere nel tempo. Nel libro, sia le entità che gli oggetti valore sono oggetti di dominio.
Risposta
nel nostro dominio problematico, abbiamo una regola aziendale che dice che un cliente può avere solo un ordine non consegnato alla volta.
Prima di andare troppo in profondità nella tana del coniglio, dovresti esaminare Greg La discussione di Young su set di coerenza , e in particolare:
Qual è limpatto aziendale di un errore?
Perché in molti casi la risposta giusta non è “t cercare di prevenire non accada la cosa sbagliata, ma invece di generare rapporti sulle eccezioni quando potrebbe esserci un problema.
Ma, presumendo che più ordini non consegnati siano una responsabilità significativa per la tua attività …
Sì, se vuoi assicurarti che ci sia un solo ordine non consegnato, è necessario che ci sia un aggregato in grado di vedere tutti gli ordini di un cliente .
Quellaggregato non è necessariamente laggregazione del cliente .
Potrebbe essere qualcosa come una coda di ordini o una cronologia di ordini, in cui tutti gli ordini per uno specifico il cliente entra nella stessa coda. Da quanto hai detto, non necessita di tutti i dati del profilo del cliente, quindi non dovrebbe “fare parte di questo aggregato.
Ma quando si tratta di spedizione, fanno tutte le loro azioni sullOrdine, non sul cliente.
Sì, quando stai effettivamente lavorando con levasione e pull sheet, la visualizzazione della cronologia non è particolarmente rilevante.
La visualizzazione della cronologia, per applicare la tua invariante, necessita solo dellID dellordine e del suo stato di elaborazione corrente. Ciò non deve necessariamente far parte dello stesso aggregato dellordine: ricorda, i confini aggregati riguardano la gestione del cambiamento, non la strutturazione delle viste.
Quindi potrebbe essere che tu gestisca lordine come un aggregato e la cronologia degli ordini come un aggregato separato e coordinare lattività tra i due.
Risposta
Hai impostato un esempio di persona di paglia. È troppo semplicistico e dubito che rifletta un sistema del mondo reale. Non modellerei quelle Entità e il loro comportamento correlato nel modo che hai specificato per questo motivo.
Le tue classi devono modellare il stato di un ordine in un modo che si riflette in più aggregati. Ad esempio, quando il cliente mette il sistema nello stato in cui deve essere elaborata la richiesta dordine del cliente, potrei creare unaggregazione di oggetti entità di dominio denominata CustomerOrderRequest
o PendingCustomerOrder
o anche solo CustomerOrder
, o qualsiasi lingua utilizzata dallazienda, potrebbe contenere un puntatore sia al cliente che a OrderLines e quindi avere un metodo come canCustomerCompleteOrder()
che viene chiamato dal livello di servizio.
Questo oggetto dominio conterrebbe la logica di business per determinare se lordine era valido o meno.
Se lordine fosse valido ed elaborato, avrei un modo per trasferire questo oggetto a un altro oggetto che rappresenti lordine elaborato.
Penso che il problema con la tua comprensione sia che stai utilizzando un esempio estremamente semplificato di aggregati. Un PendingOrder
può essere il proprio aggregato separato da un UndeliveredOrder
e di nuovo separato da un o CancelledOrder
o qualsiasi altra cosa.
Commenti
- Anche se il tuo tentativo in un linguaggio neutro rispetto al genere è divertente, vorrei notare che le donne non stanno mai nei campi per spaventare i corvi.
- @RobertHarvey che ‘ è una cosa strana su cui concentrarsi nel mio post. Spaventapasseri ed effigi sono apparsi regolarmente in forma femminile nel corso della storia.
- Non avresti ‘ fatto la distinzione nel tuo post se non lavessi ‘ lo reputo importante. Per una questione di linguistica, il termine è ” uomo di paglia; ” qualsiasi riserva sul sessismo è quasi certamente superata dal ” di cosa diavolo sta parlando ” fattore creato inventando il tuo termine.
- @RobertHarvey se qualcuno sa quale cannuccia uomo significa, ‘ sono sicuro che possano capire cosa significa persona di paglia se non hanno ‘ sentito quel termine. Possiamo concentrarci sulla sostanza del mio post, per favore, riguardo al software?
Risposta
Vaughn Vernon lo menziona nel suo libro “Implementing Domain-Driven Design” allinizio del Capitolo 7 (Servizi):
“Spesso la migliore indicazione che dovresti creare un Servizio nel modello di dominio è quando loperazione che devi eseguire sembra fuori del posto come metodo su un oggetto aggregato o valore “.
Quindi, in questo caso potrebbe esserci un servizio di dominio chiamato “CreateOrderService” che accetta unistanza del cliente e lelenco di articoli per lordine.
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 } }
Commenti
- Puoi spiegare di più in che modo il servizio di dominio può aiutare a risolvere i problemi di concorrenza nella domanda?
Lascia un commento