Non così semplice. ProcessType è più simile a un oggetto knowledge layer - definisce un determinato processo. Il processo d'altra parte è un'istanza di un processo che è ProcessType. Probabilmente non hai davvero bisogno o vuoi la relazione bidirezionale. Il processo non è probabilmente un figlio logico di un ProcessType. Tipicamente appartengono a qualcos'altro, come un prodotto, o fabbrica o sequenza.
Anche per definizione quando si elimina una radice aggregata si eliminano tutti i membri dell'aggregato. Quando si elimina un processo, dubito seriamente di voler veramente cancellare ProcessType. Se hai eliminato ProcessType, potresti voler eliminare tutti i Processi di quel tipo, ma quella relazione non è già ideale e probabilmente non eliminerai mai gli oggetti definizione non appena avrai un Processo storico definito da ProcessType.
Rimuoverei la raccolta Processi da ProcessType e trovo un genitore più adatto se ne esiste uno. Manterrei il ProcessType come membro di Process poiché probabilmente definisce Process. Gli oggetti Operational Layer (Process) e Knowledge Layer (ProcessType) funzionano raramente come un singolo aggregato, quindi avrei Process essere una radice aggregata o possibilmente trovare una radice aggregata che sia un genitore per il processo. Quindi ProcessType sarebbe una classe esterna. Process.Type è molto probabilmente ridondante poiché hai già Process.ProcessType. Liberati di quello.
Ho un modello simile per l'assistenza sanitaria. Esiste una procedura (livello operativo) e un tipo di procedura (livello della conoscenza). ProcedureType è una classe autonoma. La procedura è un figlio di un terzo oggetto Incontro. L'incontro è la radice aggregata per la procedura. La procedura ha un riferimento a ProcedureType ma è a senso unico. ProcedureType è un oggetto definizione che non contiene una raccolta di procedure.
EDIT (perché i commenti sono così limitati)
Una cosa da tenere a mente attraverso tutto questo. Molti sono puristi del DDD e irremovibili riguardo alle regole. Tuttavia, se leggi attentamente Evans, egli solleva costantemente la possibilità che vengano spesso richiesti compromessi. Fa anche molto per caratterizzare le decisioni di progettazione logiche e attentamente pensate contro cose come le squadre che non capiscono gli obiettivi o aggirano le cose come aggregati per comodità.
Le cose importanti è capire e applicare i concetti in contrasto con le regole.Vedo molti DDD che scaricano un'applicazione in aggregati illogici e confusi ecc. Per nessun'altra ragione che perché viene applicata una regola letterale su repository o attraversamento, che non è l'intento di DDD ma è spesso il prodotto dell'approccio eccessivamente dogmatico di molti prendere.
Ma quali sono i concetti chiave qui:
Aggregati forniscono un mezzo per fare un sistema complesso più gestibile riducendo i comportamenti di molti oggetti in comportamenti più alto livello dei giocatori chiave.
Gli aggregati forniscono un mezzo per garantire che gli oggetti vengano creati in una condizione logica e sempre valida che preservi anche un'unità di lavoro logica tra gli aggiornamenti e le eliminazioni.
Consideriamo l'ultimo punto. In molte applicazioni convenzionali, qualcuno crea un insieme di oggetti che non sono completamente popolati perché devono solo aggiornare o utilizzare alcune proprietà. Il prossimo sviluppatore arriva e ha bisogno anche di questi oggetti, e qualcuno ha già realizzato un set da qualche parte nel quartiere per uno scopo diverso. Ora questo sviluppatore decide di usare solo quelli, ma poi scopre che non hanno tutte le proprietà di cui ha bisogno. Quindi aggiunge un'altra query e compila alcune altre proprietà. Alla fine perché il team non aderisce a OOP perché assume l'atteggiamento comune che l'OOP è "inefficiente e poco pratico per il mondo reale e causa problemi di prestazioni come la creazione di oggetti completi per l'aggiornamento di una singola proprietà". Quello che finiscono con è un'applicazione piena di codice SQL incorporato e oggetti che si materializzano in modo casuale in qualsiasi luogo. Ancor peggio questi oggetti sono dei proxy bastardi non validi. Un processo sembra essere un processo, ma non lo è, è parzialmente popolato in modi diversi in un dato punto a seconda di ciò che era necessario. Si finisce con una palla fangosa di numerose query per riempire parzialmente in modo continuo oggetti a vari gradi e spesso un sacco di schifezze estranee come controlli nulli che non dovrebbero esistere ma sono richiesti perché l'oggetto non è mai veramente valido ecc.
Regole aggregate impedirlo assicurando che gli oggetti vengano creati solo in determinati punti logici e sempre con un set completo di relazioni e condizioni valide. Quindi, ora che capiamo perfettamente a quali regole aggregate ci sono e da cosa ci proteggono, vogliamo anche capire che non vogliamo abusare di queste regole e creare strani aggregati che non riflettono ciò di cui la nostra applicazione si riferisce semplicemente perché queste regole aggregate esistono e devono essere seguite in ogni momento.
Così quando Evans dice di creare Repository solo per aggregati, sta dicendo di creare aggregati in uno stato valido e di mantenerli in questo modo invece di aggirare l'aggregato direttamente per gli oggetti interni. Hai un processo come aggregato di root in modo da creare un repository. ProcessType non fa parte di questo aggregato. cosa fai? Bene, se un oggetto è di per sé ed è un'entità, è un aggregato di 1. Si crea un repository per questo.
Ora il purista arriverà e dirà che non si dovrebbe avere quel repository perché ProcessType è un oggetto valore, non un'entità. Pertanto ProcessType non è affatto un aggregato e pertanto non si crea un repository per questo. Allora cosa fai? Quello che non si fa è calzare il ProcessType in una sorta di modello artificiale per nessun'altra ragione che non è necessario ottenerlo in modo da avere bisogno di un repository ma per avere un repository bisogna avere un'entità come root aggregato. Quello che fai è considerare attentamente i concetti. Se qualcuno ti dice che il repository è sbagliato, ma sai che ne hai bisogno e qualunque cosa possano dire, il tuo sistema di repository è valido e conserva i concetti chiave, mantieni il repository come invece di deformare il tuo modello per soddisfare i dogmi.
Ora, supponendo che io sia corretto su cosa sia ProcessType, come ha notato l'altro commentatore, si tratta in realtà di un oggetto valore. Tu dici che non può essere un oggetto valore. Questo potrebbe essere per diverse ragioni.Forse lo dici perché utilizzi NHibernate ad esempio, ma il modello di NHibernate per l'implementazione di oggetti valore nella stessa tabella di un altro oggetto non funziona. Quindi il tuo ProcessType richiede una colonna e un campo di identità. Spesso a causa delle considerazioni sul database, l'unica implementazione pratica consiste nell'avere oggetti value con id nella propria tabella. O forse lo dici perché ogni processo punta a un singolo ProcessType per riferimento.
Non importa. È un oggetto di valore a causa del concetto. Se si hanno 10 oggetti Process che sono dello stesso ProcessType, si hanno 10 membri e valori Process.ProcessType. Indipendentemente dal fatto che ogni Process.ProcessType punta a un singolo riferimento, o che ognuno abbia una copia, dovrebbero comunque essere tutti per definizione esattamente identici e completamente intercambiabili con gli altri 10. Questo è ciò che lo rende un oggetto Object. La persona che dice "Ha un ID quindi non può essere un valore L'oggetto che hai un'entità" sta facendo un errore dogmatico. Non fare lo stesso errore, se hai bisogno di un campo ID dargli uno, ma non dire "non può essere un oggetto valore" quando in realtà è anche se uno che per altro motivo hai dovuto dare un ID a.
Quindi come si ottiene questo giusto e sbagliato? ProcessType è un oggetto valore, ma per qualche motivo è necessario avere un ID. L'ID di per sé non viola le regole. Hai capito bene con 10 processi che hanno tutti un ProcessType che è esattamente lo stesso. Forse ognuno ha una copia locale di Deeep, forse indicano tutti un oggetto. ma ognuno è identico in entrambi i casi, ad esempio ognuno ha un Id = 2, per esempio. Si ottiene è sbagliato quando si esegue questa operazione: 10 processi ciascuno hanno un ProcessType e questo ProcessType è identico e completamente intercambiabile ECCETTO ora ognuno ha anche il proprio ID univoco. Ora hai 10 istanze della stessa cosa ma variano solo in ID e variano sempre solo in Id. Ora non hai più un oggetto valore, non perché gli hai dato un ID, ma perché hai dato un ID con un'implementazione che riflette la natura di un'entità - ogni istanza è unica e diversa
Ha senso?
Approccio molto interessante David. Grazie per averlo condiviso! Come hai implementato il LookupService? Come servizio di infrastruttura con un metodo Get generico che utilizza direttamente il framework ORM sottostante senza repository? Qualcun altro può commentare questo approccio? Hai fatto qualcosa di simile o hai risolto questo problema in un modo diverso? – Vern
Ho definito ILookupService nella mia infrastruttura, quindi un'implementazione in un progetto separato che viene caricato usando DI (unity). L'implementazione è raw SQL/Sprocs, ma ovviamente è possibile utilizzare facilmente un ORM. Questo servizio può essere utilizzato dal dominio e dai servizi di query per fornire ViewModels all'interfaccia utente. Il motivo per cui utilizzo i generici è perché tutti i 'LookupItems' hanno semplicemente un ID, una descrizione e un elenco di attributi. Ciò significa che posso utilizzare un metodo generico per archiviare i dati per tutte le ricerche/elementi di ricerca utilizzando il loro nome di tipo. OSSIA Non avrei una tabella chiamata 'ProcessType'. –
Vedo. Ho appena provato a implementarlo e sembra una soluzione elegante. Per ora seguirò questo approccio a meno che qualcuno non arrivi e ci dice che hanno una soluzione migliore :-) Lascerò che la domanda rimanga aperta per alcuni giorni per ottenere più opinioni su questo argomento. – Vern