I DDD Aggregates sono davvero una buona idea in un'applicazione Web?

37

Mi sto immergendo in Domain Driven Design e alcuni dei concetti che sto incontrando hanno molto senso in superficie, ma quando ci penso di più mi chiedo se sia davvero una buona idea.

Il concetto di Aggregati, ad esempio, ha senso. Crei piccoli domini di proprietà in modo da non dover gestire l'intero modello di dominio.

Tuttavia, quando penso a questo nel contesto di un'applicazione web, stiamo frequentemente colpendo il database per recuperare piccoli sottoinsiemi di dati. Ad esempio, una pagina può solo elencare il numero di ordini, con i collegamenti per fare clic su per aprire l'ordine e vedere i suoi ID ordine.

Se ho capito bene gli Aggregati, normalmente userei il modello di repository per restituire un OrderAggregate che conterrebbe i membri GetAll , GetByID , Delete e Save . Ok suona bene. Ma ...

Se chiamo GetAll per elencare tutti i miei ordini, mi sembra che questo modello richiederebbe l'intero elenco di informazioni aggregate da restituire, completare ordini, righe d'ordine, ecc ... Quando ho solo bisogno di un piccolo sottoinsieme di tali informazioni (solo informazioni di intestazione).

Mi manca qualcosa? O c'è qualche livello di ottimizzazione che useresti qui? Non posso immaginare che qualcuno sosterrebbe il ritorno di interi aggregati di informazioni quando non ne hai bisogno.

Certamente, si potrebbero creare metodi sul repository come GetOrderHeaders , ma ciò sembra vanificare lo scopo di utilizzare un repository come il pattern in primo luogo.

Qualcuno può chiarirlo per me?

Modifica

Dopo molte più ricerche, penso che la disconnessione qui sia che un modello di Repository puro è diverso da quello che la maggior parte delle persone pensa di un Repository.

Fowler definisce un repository come un archivio dati che utilizza la semantica della raccolta e viene generalmente conservato in memoria. Ciò significa creare un intero grafo degli oggetti.

Evans modifica il repository per includere i root aggregati e quindi il repository viene amputato per supportare solo gli oggetti in un aggregato.

La maggior parte delle persone sembra pensare ai repository come agli oggetti di accesso ai dati glorificati, dove basta creare metodi per ottenere qualsiasi dato desiderato. Questo non sembra essere l'intento descritto nei modelli di Fowler's Enterprise Architecture.

Altri ancora pensano a un repository come a una semplice astrazione utilizzata principalmente per rendere più facile la verifica e la simulazione, o per dissociare la persistenza dal resto del sistema.

Credo che la risposta sia che questo è un concetto molto più complesso di quanto pensassi all'inizio.

    
posta Erik Funkenbusch 13.02.2011 - 22:04
fonte

6 risposte

29

Non utilizzare il modello di dominio e gli aggregati per le query.

In effetti, ciò che stai chiedendo è una domanda abbastanza comune che è stato stabilito un insieme di principi e modelli per evitare solo quello. Si chiama CQRS .

    
risposta data 14.02.2011 - 02:02
fonte
8

Ho faticato e sto ancora lottando con il modo migliore per utilizzare il modello di repository in un Domain Driven Design. Dopo averlo usato per la prima volta, ho trovato le seguenti pratiche:

  1. Un repository dovrebbe essere semplice; è responsabile solo per la memorizzazione di oggetti di dominio e il loro recupero. Tutte le altre logiche dovrebbero trovarsi in altri oggetti, come fabbriche e servizi di dominio.

  2. Un repository si comporta come una raccolta come se fosse una raccolta in memoria di root aggregati.

  3. Un repository non è un DAO generico, ogni repository ha un'interfaccia unica e ristretta. Un repository ha spesso metodi specifici di ricerca che ti permettono di cercare la collezione in termini di dominio (ad esempio: dammi tutti gli ordini aperti per l'utente X). Il repository stesso può essere implementato con l'aiuto di un DAO generico.

  4. Idealmente i metodi finder restituiranno solo radici aggregate. Se ciò è inefficiente, può anche restituire oggetti con valore di sola lettura che contengono esattamente ciò che è necessario (anche se è un vantaggio se questi oggetti valore possono anche essere espressi in termini di dominio). Come ultima risorsa, il repository può anche essere utilizzato per restituire sottoinsiemi o raccolte di sottoinsiemi di una radice aggregata.

  5. Scelte come queste dipendono dalle tecnologie utilizzate, poiché è necessario trovare il modo di esprimere il tuo modello di dominio in modo più efficiente con le tecnologie utilizzate.

risposta data 24.04.2011 - 23:52
fonte
6

Non penso che il tuo metodo GetOrderHeaders sconfigga affatto lo scopo del repository.

DDD è interessato (tra le altre cose) a garantire che tu ottenga ciò di cui hai bisogno tramite la radice aggregata (non avresti un OrderDetailsRepository, ad esempio), ma non ti limita nel modo in cui sei di nota.

Se OrderHeader è un concetto di dominio, è necessario definirlo come tale e disporre dei metodi di repository appropriati per recuperarli. Assicurati di passare attraverso la radice aggregata corretta quando lo fai.

    
risposta data 14.02.2011 - 03:48
fonte
4

Il mio uso di DDD non può essere considerato DDD "puro", ma ho adattato le seguenti strategie del mondo reale usando DDD contro un archivio dati DB.

  • Una radice aggregata ha un associato repository
  • Il repository associato è usato solo da quella radice aggregata (non è disponibile pubblicamente)
  • Un repository può contenere chiamate di query (ad esempio, GetAllActiveOrders, GetOrderItemsForOrder)
  • Un servizio espone un sottoinsieme pubblico del repository e altre operazioni non-crude (ad esempio, trasferimento di denaro da un conto bancario a un altro, LoadById, Cerca / Trova, CreaEntity, ecc.).
  • Io uso la radice - > Servizio - > Stack del repository. Si suppone che un servizio DDD sia utilizzato per qualsiasi cosa che un'entità non può rispondere autonomamente (ad esempio, LoadById, TransferMoneyFromAccountToAccount), ma nel mondo reale tendo ad attenersi ad altri servizi relativi a CRUD (Salva, Elimina, Domanda) anche se il root dovrebbe essere in grado di "rispondere / eseguire" questi stessi. Nota che non c'è niente di sbagliato nel dare a un'entità l'accesso ad un altro servizio di root aggregato! Tuttavia, ricorda che non includeresti in un servizio (GetOrderItemsForOrder) ma includeresti quello nel repository in modo che il Radice aggregato possa farne uso. Si noti che un servizio non deve esporre alcuna query aperta come può fare il repository.
  • Di solito definisco un repository in modo astratto nel modello di dominio (tramite interfaccia) e fornisco un'implementazione concreta separata. Definisco completamente un servizio nel modello di dominio che si inserisce in un repository concreto per il suo utilizzo.

** Non è necessario riportare un intero aggregato. Tuttavia, se vuoi di più devi chiedere la root, non qualche altro servizio o repository. Si tratta di un caricamento lazy e può essere eseguito manualmente con il caricamento lazy di un uomo povero (iniettando il repository / il servizio appropriato nella root) o utilizzando e ORM che lo supporta.

Nel tuo esempio, probabilmente fornirei una chiamata al repository che portasse solo le intestazioni degli ordini se volessi caricare i dettagli su una chiamata separata. Nota che avendo un "OrderHeader" stiamo introducendo un concetto aggiuntivo nel dominio.

    
risposta data 07.03.2011 - 06:16
fonte
3

Il tuo modello di dominio contiene la tua logica aziendale nella sua forma più pura. Tutte le relazioni e le operazioni che supportano le operazioni aziendali. Quello che ti manca dalla tua mappa concettuale è l'idea del Layer del servizio applicativo il livello di servizio si avvolge attorno al modello di dominio e fornisce una vista semplificata del dominio aziendale (una proiezione se lo si desidera) che consente al modello di dominio di cambiare secondo necessità senza incidere direttamente sulle applicazioni che utilizzano il livello di servizio.

Andando oltre. L'idea dell'aggregato è che esiste un oggetto, la radice aggregata, responsabile del mantenimento della coerenza dell'aggregato. Nel tuo esempio, l'ordine sarebbe responsabile della manipolazione delle sue righe di ordine.

Per il tuo esempio, il livello di servizio esporrebbe un'operazione come GetOrdersForCustomer che restituirebbe solo ciò che è necessario per visualizzare un elenco riepilogativo degli ordini (come li chiami OrderHeaders).

Infine, il pattern Repository non è SOLO una collezione, ma consente anche query dichiarative. In C # puoi utilizzare LINQ come Oggetto query , o la maggior parte degli altri O / RM fornisce anche una specifica dell'oggetto Query.

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. (from Fowler's Repository page)

Visto che è possibile creare query sul repository, ha anche senso fornire metodi di comodità che gestiscono query comuni. Cioè se vuoi solo le intestazioni del tuo ordine, puoi creare una query che restituisce solo l'intestazione e esporla da un metodo comodo nei tuoi repository.

Spero che questo aiuti a chiarire le cose.

    
risposta data 31.10.2014 - 16:27
fonte
0

So che questa è una vecchia domanda, ma sembra che sia arrivata a una risposta diversa.

Quando creo un repository generalmente si avviano alcune query memorizzate nella cache .

Fowler defines a repository as a data store that uses collection semantics, and is generally kept in-memory. This means creating an entire object graph.

Mantieni quei repository nella ram del tuo server. Non passano solo oggetti nel database!

Se sono in una applicazione web con una pagina che elenca gli ordini, puoi fare clic su per vedere i dettagli, è probabile che io voglia che la mia pagina di elenco degli ordini abbia i dettagli sugli ordini (ID, Nome, Quantità , Data) per aiutare un utente a decidere quale guardare.

A questo punto hai due opzioni.

  1. È possibile interrogare il database e richiamare esattamente ciò che è necessario per fare la lista, quindi eseguire nuovamente la query per estrarre i singoli dettagli che dovresti vedere nella pagina dei dettagli.

  2. È possibile effettuare 1 query che richiama tutte le informazioni e le memorizza nella cache. Nella richiesta della pagina successiva si legge dalla ram dei server invece del database. Se l'utente risponde o seleziona la pagina successiva, stai ancora effettuando zero viaggi nel database.

In realtà, come lo si implementa è proprio questo e i dettagli di implementazione. Se il mio più grande utente ha 10 ordini, probabilmente voglio andare con l'opzione 2. Se sto parlando di 10.000 ordini, è necessaria l'opzione 1. In entrambi i casi di cui sopra e in molti altri casi voglio che il repository nasconda i dettagli di questa implementazione.

Andando avanti se ottengo un ticket per comunicare all'utente quanto ha speso per gli ordini ( dati aggregati ) nell'ultimo mese nella pagina di elenco degli ordini, preferirei scrivere la logica per calcolare quello in SQL e fai ancora un altro round trip nel DB o preferisci calcolarlo usando i dati che sono già nella ram dei server?

Nella mia esperienza, gli aggregati di domini offrono enormi vantaggi.

  • Sono un enorme riutilizzo del codice che funziona davvero.
  • Semplificano il codice mantenendo la logica aziendale direttamente nel livello principale invece di dover eseguire il drill-through di un livello infrastruttura per ottenere il server sql per farlo.
  • Inoltre, possono velocizzare notevolmente i tempi di risposta riducendo il numero di query da eseguire poiché è possibile memorizzarle facilmente.
  • L'SQL che sto scrivendo è spesso molto più gestibile dal momento che spesso chiedo tutto e calcola il lato server.
risposta data 05.11.2018 - 23:00
fonte

Leggi altre domande sui tag