Dopo aver letto i libri di Evan e Nilsson, non sono ancora sicuro su come gestire l'accesso ai dati in un progetto gestito da un dominio. I metodi CRUD dovrebbero essere parte dei repository, ad esempio OrderRepository.GetOrdersByCustomer (cliente) o dovrebbero far parte delle entità: Customer.GetOrders(). Quest'ultimo approccio sembra più OO, ma distribuirà Data Access per un singolo tipo di entità tra più oggetti, ad esempio Customer.GetOrders(), Invoice.GetOrders(), ShipmentBatch.GetOrders(), ecc. Che dire di inserimento e aggiornamento?accesso ai dati in DDD?
risposta
I metodi CRUD-ish devono essere parte del repository ... ish. Ma penso che dovresti chiedere perché hai un sacco di metodi CRUD. Cosa fanno veramente ? Cosa sono davvero? Se in effetti richiami i pattern di accesso ai dati utilizzati dalla tua applicazione, penso che renda il repository molto più utile e ti impedisca di eseguire un intervento chirurgico con fucile a pompa quando certi tipi di cambiamenti accadono nel tuo dominio.
CustomerRepo.GetThoseWhoHaventPaidTheirBill()
// or
GetCustomer(new HaventPaidBillSpecification())
// is better than
foreach (var customer in GetCustomer()) {
/* logic leaking all over the floor */
}
Anche i metodi di tipo "Salva" devono far parte del repository.
Se avete radici di aggregazione, questo ti impedisce di avere un'esplosione Repository, o avere la logica sparsi in tutto: Non si dispone di 4 x # di modelli di accesso entità di dati, solo quelle effettivamente utilizzate sull'aggregato radici.
Questo è il mio $ 0,02.
Anche in un DDD, terrei le classi e le routine di accesso ai dati separate dalle entità.
Le ragioni sono,
- Testabilità migliora
- separazione degli interessi e del design modulare
- più mantenibile nel lungo periodo, quando si aggiungono le entità, le routine
non sono un esperto , solo la mia opinione.
La cosa fastidiosa con l'applicazione DDD di Nilsson & P è che inizia sempre con "Non lo farei in un'applicazione del mondo reale ma ..." e quindi segue il suo esempio. Torna all'argomento: Penso che OrderRepository.GetOrdersByCustomer (cliente) sia la strada da percorrere, ma c'è anche una discussione sulla mailing list ALT.Net (http://tech.groups.yahoo.com/group/altdotnet/) su DDD.
Ho fatto in entrambi i modi di cui stai parlando, Il mio approccio preferito ora è il persistente ignorante (o PONO - Plain Ole '.Net Object) metodo in cui le classi di dominio sono solo preoccupati di essere classi di dominio. Non sanno nulla di come sono persi o anche se sono persistenti. Ovviamente a volte devi essere un po 'pragmatico e ammettere cose come un Id (ma anche in questo caso uso solo un layer di tipo super che ha l'Id in modo da avere un singolo punto in cui vivono cose come il valore predefinito)
Il motivo principale è che mi sforzo di seguire il principio della responsabilità unica. Seguendo questo principio ho trovato il mio codice molto più verificabile e mantenibile. È anche molto più facile apportare modifiche quando sono necessarie poiché ho solo una cosa a cui pensare.
Una cosa da tenere d'occhio è il metodo gonfio di cui i repository possono soffrire. GetOrderbyCustomer .. GetAllOrders .. GetOrders30DaysOld .. ecc. Ecc. Una buona soluzione a questo problema è osservare il modello dell'oggetto di query. E poi i tuoi repository possono solo prendere un oggetto query da eseguire.
Consiglio vivamente di esaminare qualcosa come NHibernate. Comprende molti dei concetti che rendono i repository così utili (Identity Map, Cache, Query objects ..)
DDD di solito preferisce il modello di repository sul modello di record attivo suggerito con Customer.Save.
Uno svantaggio del modello Active Record è che presume praticamente un singolo modello di persistenza, salvo alcuni codici particolarmente intrusivi (nella maggior parte delle lingue).
L'interfaccia del repository è definita nel livello dominio, ma non sa se i dati sono memorizzati in un database o meno. Con il pattern di repository, posso creare un InMemoryRepository in modo che possa testare la logica di dominio in isolamento e utilizzare dependency injection nell'applicazione per fare in modo che il livello di servizio istanzia un SqlRepository, ad esempio.
Per molte persone, disporre di un repository speciale solo per testare i suoni è sciocco, ma se si utilizza il modello di repository, è possibile che non si abbia realmente bisogno di un database per la propria applicazione specifica; a volte un semplice FileRepository farà il trucco. Matrimonio a te stesso in un database prima che tu sappia che ne hai bisogno è potenzialmente limitante. Anche se è necessario un database, è molto più veloce eseguire test su un InMemoryRepository.
Se non si dispone molto della logica del dominio, probabilmente non è necessario il DDD. ActiveRecord è abbastanza adatto per molti problemi, specialmente se si hanno per lo più dati e solo un po 'di logica.
Facciamo un passo indietro per un secondo. Evans raccomanda che i repository restituiscano radici aggregate e non solo entità. Pertanto, supponendo che il cliente sia una radice aggregata che include gli ordini, quindi quando si è recuperato il cliente dal proprio repository, gli ordini sono arrivati insieme a esso. Accederai agli ordini navigando nella relazione dal Cliente agli Ordini.
customer.Orders;
Quindi per rispondere alla tua domanda, le operazioni CRUD sono presenti su repository radice aggregati.
CustomerRepository.Add(customer);
CustomerRepository.Get(customerID);
CustomerRepository.Save(customer);
CustomerRepository.Delete(customer);