Mi sto immergendo nei concetti di Domain-Driven Design (DDD) e ho trovato alcuni principi strani, soprattutto per quanto riguarda l'isolamento del dominio e il modello di persistenza. Ecco la mia comprensione di base:
- Un servizio a livello di applicazione (che fornisce un set di funzionalità) richiede oggetti di dominio da un repository necessario per svolgere la sua funzione.
- L'implementazione concreta di questo repository recupera i dati dall'archivio per il quale è stata implementata
- Il servizio dice all'oggetto dominio, che incapsula la logica aziendale, di eseguire determinate attività che ne modificano lo stato.
- Il servizio dice al repository di persistere l'oggetto dominio modificato.
- Il repository deve mappare l'oggetto di dominio sulla rappresentazione corrispondente nella memoria.
Ora, dati i presupposti di cui sopra, i seguenti sembrano imbarazzanti:
Annuncio 2 .:
Il modello di dominio sembra caricare l'intero oggetto di dominio (inclusi tutti i campi e i riferimenti), anche se non sono necessari per la funzione che lo ha richiesto. Il caricamento completo potrebbe non essere nemmeno possibile se si fa riferimento ad altri oggetti di dominio, a meno che non vengano caricati anche quegli oggetti di dominio e tutti gli oggetti a cui fanno riferimento a turno, e così via e così via. Viene in mente il caricamento lento, il che significa tuttavia che si inizia a interrogare gli oggetti del dominio che dovrebbero essere in primo luogo responsabilità del repository.
Dato questo problema, il modo "corretto" di caricare oggetti di dominio sembra avere una funzione di caricamento dedicata per ogni caso d'uso. Queste funzioni dedicate caricheranno quindi solo i dati richiesti dal caso d'uso per cui sono stati progettati. Ecco dove entra in gioco l'imbarazzo: in primo luogo, dovrei mantenere una notevole quantità di funzioni di caricamento per ogni implementazione del repository, e gli oggetti del dominio finirebbero in stati incompleti che si svolgono null
nei loro campi. Quest'ultimo non dovrebbe tecnicamente essere un problema perché se un valore non è stato caricato, non dovrebbe essere richiesto dalla funzionalità che lo ha richiesto comunque. Tuttavia è imbarazzante e un potenziale pericolo.
Annuncio 3 .:
In che modo un oggetto dominio verificherebbe i vincoli di unicità sulla costruzione se non ha alcuna nozione di repository? Ad esempio, se volessi crearne uno nuovo User
con un numero univoco di previdenza sociale (che viene fornito), il primo conflitto si verificherebbe chiedendo al repository di salvare l'oggetto, solo se nel database è stato definito un vincolo di unicità. Altrimenti, potrei cercare un User
con la previdenza sociale fornita e segnalare un errore nel caso esista, prima di crearne uno nuovo. Ma i controlli dei vincoli vivrebbero nel servizio e non nell'oggetto dominio a cui appartengono. Mi sono appena reso conto che agli oggetti del dominio è consentito utilizzare i repository (iniettati) per la convalida.
Annuncio 5 .:
Percepisco il mapping degli oggetti di dominio su un back-end di archiviazione come un processo ad alta intensità di lavoro rispetto al fatto che gli oggetti di dominio modificano direttamente i dati di base. Naturalmente, è un prerequisito essenziale per disaccoppiare l'implementazione di archiviazione concreta dal codice di dominio. Tuttavia, ha davvero un costo così elevato?
Apparentemente hai la possibilità di usare gli strumenti ORM per fare il mapping per te. Ciò richiederebbe spesso di progettare il modello di dominio in base alle restrizioni dell'ORM, o di introdurre una dipendenza dal dominio al livello dell'infrastruttura (utilizzando ad esempio le annotazioni ORM negli oggetti di dominio). Inoltre ho letto che gli ORM introducono un notevole sovraccarico computazionale.
Nel caso dei database NoSQL, per i quali non esistono quasi concetti simili a ORM, come tenere traccia delle proprietà modificate nei modelli di dominio save()
?
Modifica : Inoltre, affinché un repository acceda allo stato dell'oggetto dominio (ovvero il valore di ciascun campo), l'oggetto dominio deve rivelare il suo stato interno che interrompe l'incapsulamento.
In generale:
- Dove andrebbe la logica transazionale? Questo è certamente specifico per la persistenza. Alcune infrastrutture di archiviazione potrebbero non supportare affatto le transazioni (come i repository fittizi in memoria).
- Per le operazioni in blocco che modificano più oggetti, dovrei caricare, modificare e archiviare ogni oggetto singolarmente per passare attraverso la logica di convalida incapsulata dell'oggetto? Ciò è contrario all'esecuzione di una singola query direttamente sul database.
Gradirei alcuni chiarimenti su questo argomento. I miei presupposti sono corretti? In caso contrario, qual è il modo corretto di affrontare questi problemi?