Perché separare l'accesso ai dati?
Dal libro, penso che le prime due pagine del capitolo Model Driven Design forniscano una giustificazione per il motivo per cui si desidera estrarre i dettagli tecnici di implementazione dall'implementazione del modello di dominio.
- Vuoi mantenere una stretta connessione tra il modello di dominio e il codice
- Separare le preoccupazioni tecniche aiuta a dimostrare che il modello è pratico per l'implementazione
- Volete che il linguaggio onnipresente permea fino alla progettazione del sistema
Questo sembra essere tutto allo scopo di evitare un "modello di analisi" separato che viene divorziato dall'attuazione effettiva del sistema.
Da quanto ho capito del libro, si dice che questo "modello di analisi" può finire per essere progettato senza considerare l'implementazione del software. Una volta che gli sviluppatori cercano di implementare il modello compreso dal lato aziendale, formano le proprie astrazioni per necessità, causando un muro nella comunicazione e nella comprensione.
Nella direzione opposta, anche gli sviluppatori che introducono troppe preoccupazioni tecniche nel modello di dominio possono causare questa divisione.
Quindi potresti considerare che praticare la separazione di preoccupazioni come la persistenza può aiutare a salvaguardare da questi progetti e modelli di analisi divergenti. Se sembra necessario introdurre elementi come la persistenza nel modello, allora è una bandiera rossa. Forse il modello non è pratico per l'implementazione.
citando:
"Il singolo modello riduce le possibilità di errore, perché il design è ora una crescita diretta del modello attentamente considerato. Il design, e persino il codice stesso, ha la comunicatività di un modello."
Il modo in cui lo sto interpretando, se hai finito con più righe di codice che trattano cose come l'accesso al database, perdi quella comunicatività.
Se la necessità di accedere a un database riguarda cose come il controllo dell'unicità, dai un'occhiata a:
Udi Dahan: i più grandi errori che i team commettono quando applicano DDD
http://gojko.net/2010/06/11/udi-dahan-the-biggest-mistakes-teams-make-when-applying-ddd/
in "Tutte le regole non sono create uguali"
e
Impiegando il modello del modello di dominio
http://msdn.microsoft.com/en-us/magazine/ee236415.aspx#id0400119
in "Scenari per non utilizzare il modello di dominio", che tocca lo stesso argomento.
Come separare l'accesso ai dati
Caricamento dei dati tramite un'interfaccia
Il "livello di accesso ai dati" è stato estratto attraverso un'interfaccia, che si chiama per recuperare i dati richiesti:
var orderLines = OrderRepository.GetOrderLines(orderId);
foreach (var line in orderLines)
{
total += line.Price;
}
Pro: L'interfaccia separa il codice idraulico di "accesso ai dati", permettendoti di scrivere ancora dei test. L'accesso ai dati può essere gestito caso per caso consentendo prestazioni migliori rispetto a una strategia generica.
Contro: il codice chiamante deve assumere ciò che è stato caricato e cosa no.
Supponiamo che GetOrderLines restituisca oggetti OrderLine con una proprietà ProductInfo null per motivi di prestazioni. Lo sviluppatore deve avere una profonda conoscenza del codice dietro l'interfaccia.
Ho provato questo metodo su sistemi reali. Si finisce per cambiare l'ambito di ciò che viene caricato continuamente nel tentativo di risolvere i problemi di prestazioni. Finisci per sbirciare dietro l'interfaccia per guardare il codice di accesso ai dati per vedere cosa è e non viene caricato.
Ora, la separazione delle preoccupazioni dovrebbe consentire allo sviluppatore di concentrarsi su un aspetto del codice alla volta, per quanto possibile. La tecnica di interfaccia rimuove il COME vengono caricati questi dati, ma NON COME SONO caricati MOLTI dati, QUANDO vengono caricati e DOVE vengono caricati.
Conclusione: separazione abbastanza bassa!
Caricamento pigro
I dati vengono caricati su richiesta. Le chiamate per caricare i dati sono nascoste nel grafico dell'oggetto stesso, dove l'accesso a una proprietà può causare l'esecuzione di una query sql prima di restituire il risultato.
foreach (var line in order.OrderLines)
{
total += line.Price;
}
Pro: "QUANDO, DOVE, e COME" dell'accesso ai dati è nascosto allo sviluppatore e si concentra sulla logica del dominio. Nell'aggregato non esiste alcun codice che si occupa del caricamento dei dati. La quantità di dati caricati può essere la quantità esatta richiesta dal codice.
Contro: quando si è colpiti da un problema di prestazioni, è difficile risolvere una soluzione generica "taglia unica". Il caricamento lento può causare prestazioni complessivamente peggiori e l'implementazione del caricamento lento può essere complicata.
Interfaccia ruolo / Recupero desideroso
Ogni caso d'uso viene reso esplicito tramite un'interfaccia ruolo implementata dalla classe aggregata, che consente di gestire le strategie di caricamento dei dati per caso d'uso.
La strategia di recupero può essere simile al seguente:
public class BillOrderFetchingStrategy : ILoadDataFor<IBillOrder, Order>
{
Order Load(string aggregateId)
{
var order = new Order();
order.Data = GetOrderLinesWithPrice(aggregateId);
return order;
}
}
Quindi il tuo aggregato può apparire come:
public class Order : IBillOrder
{
void BillOrder(BillOrderCommand command)
{
foreach (var line in this.Data.OrderLines)
{
total += line.Price;
}
etc...
}
}
BillOrderFetchingStrategy viene utilizzato per creare l'aggregato, quindi l'aggregato fa il suo lavoro.
Pro: consente il codice personalizzato per caso d'uso, consentendo prestazioni ottimali. È in linea con il principio di segregazione dell'interfaccia . Nessun requisito di codice complesso. I test unitari aggregati non devono imitare la strategia di caricamento. La strategia di caricamento generica può essere utilizzata per la maggior parte dei casi (ad esempio una strategia "carica tutto") e, se necessario, possono essere implementate strategie di caricamento speciali.
Contro: lo sviluppatore deve ancora modificare / rivedere la strategia di recupero dopo aver modificato il codice di dominio.
Con l'approccio della strategia di recupero potresti ancora ritrovarti a modificare il codice di recupero personalizzato per un cambiamento nelle regole aziendali. Non è una separazione perfetta delle preoccupazioni, ma finirà per essere più mantenibile ed è migliore della prima opzione. La strategia di recupero incapsula i dati HOW, WHEN e WHERE caricati. Ha una migliore separazione delle preoccupazioni, senza perdere la flessibilità come l'unica soluzione adatta a tutti i metodi di caricamento lento.