DDD - Radice aggregata con un gran numero di figli


10

Prefarrò questa domanda dicendo che sono relativamente nuovo con DDD, quindi potrei fare alcuni errori fondamentali qui!

Sto lavorando a un progetto che coinvolge i concetti di contabilità e transazioni (in senso finanziario). Un Conto può avere molte Transazioni inserite contro di esso.

Mi sembra che il Conto e la Transazione siano entrambi Entità e che il Conto sia una radice aggregata contenente Transazioni poiché una Transazione non può esistere senza il Conto.

Tuttavia, quando arrivo ad applicare questo codice, ho subito riscontrato un problema. In molte situazioni non è particolarmente utile per me avere un elenco di ogni Transazione in un Conto in ogni momento. Sono interessato a essere in grado di fare cose come il calcolo del saldo del Conto e l'applicazione di invarianti come un limite di credito, ma voglio anche essere in grado di lavorare facilmente con un sottoinsieme di Transazioni (ad es. Visualizzare quelli che rientrano in un intervallo di date).

In quest'ultimo caso, se stavo usando un, TransactionRepositorypotrei accedere in modo efficiente solo a quegli oggetti di cui ho bisogno senza caricare l'intero elenco (potenzialmente molto grande). Tuttavia, ciò consentirebbe a cose diverse dall'account di funzionare con le transazioni, il che significa che ho rotto il concetto di account come radice aggregata.

In che modo le persone gestiscono questo tipo di situazione? Accettate semplicemente le implicazioni di memoria e prestazioni del caricamento di un numero potenzialmente enorme di figli per una radice aggregata?

Risposte:


9

Consiglierei di stare attento con la regola "non puoi esistere senza" . Questo parla del concetto di composizione nel design UML / OO e potrebbe essere stato uno degli approcci prescritti per la progettazione di aggregati nel libro blu originale DDD (non ne sono sicuro) ma da allora è stato ampiamente rivisto. Potrebbe essere un'idea migliore vedere i tuoi aggregati da una prospettiva di confine di coerenza transazionale .

L'idea è di non rendere i tuoi aggregati troppo grandi, dove potresti avere problemi di prestazioni come quello che hai sottolineato, né troppo piccoli, perché alcuni invarianti si estendono inevitabilmente a più aggregati, causando problemi di blocco degli aggregati e di concorrenza.

La giusta dimensione aggregata corrisponderebbe idealmente ai contorni di ciò che si modifica in una determinata transazione commerciale, né più né meno. Nel tuo esempio, se non ci sono molti invarianti di dominio che si estendono su più transazioni finanziarie, fare Transactionuna radice aggregata a sé stante potrebbe essere la soluzione migliore.


Grazie, rileggerò i limiti di coerenza. Penso che il tuo suggerimento di fare della Transazione la propria radice aggregata possa essere valido; come dici tu non ho molti invarianti che coprono più transazioni.
krixon,

7

tl; dr - rompi le regole se necessario. DDD non può risolvere tutti i problemi; in effetti le idee oggetto che fornisce sono buoni consigli e un buon inizio, ma scelte davvero cattive per alcuni problemi aziendali. Consideralo un suggerimento su come fare le cose.


Per il problema del caricamento di tutti i figli (transazione) con il genitore (account) - Sembra che tu abbia riscontrato il problema n + 1 (qualcosa su Google) che molti ORM hanno risolto.

Puoi risolverlo caricando lentamente i bambini (transazione) - solo quando necessario.

Ma sembra che tu sappia già che menzionando che puoi usare un TransactionRepository per risolvere il problema.

Per "nascondere" quei dati in modo che solo l'Account possa utilizzarli, non dovresti nemmeno memorizzarli dove nessun altro vorrebbe come deserializzarli, come una tabella relazionale pubblica. Potresti averlo archiviato con il "documento" dell'account in un DB di documento. In ogni caso, se qualcuno ci provasse abbastanza, potrebbe comunque vedere i dati. E 'lavorare' con esso. E quando non stai guardando, lo faranno!

Quindi potresti impostare le autorizzazioni, ma poi devi eseguire 'account' come processo separato.

Quello che ti rendi davvero conto qui è che DDD e l'uso puro del modello a oggetti ti riconducono a volte in un angolo. Sinceramente, ovviamente, non è necessario utilizzare la 'composizione' / radice aggregata per beneficiare dei principi di progettazione di DDD. È solo una cosa che puoi usare quando hai una situazione che si adatta ai suoi vincoli.

Qualcuno potrebbe dire "non ottimizzare in anticipo". Qui in questo caso, però, conosci la risposta: ci saranno transazioni sufficienti per impantanare un metodo che li manterrà per sempre con l'account.

La vera risposta è iniziare a stare in piedi SOA. Nel mio posto di lavoro abbiamo guardato i video di Udi Dahan "Distributed computing" e abbiamo acquistato nServiceBus (solo la nostra scelta). Crea un servizio per gli account - con il suo processo, le code dei messaggi, l'accesso a un database di relazioni che solo lui può vedere e ... viola, potresti codificare le istruzioni SQL nel programma e persino lanciare un paio di script di transazione Cobol (scherzando ovviamente) ma seriamente hanno più separazione delle preoccupazioni di quanto lo snob OO / Java più intelligente possa mai sognare.

Consiglierei comunque di modellarlo bene; puoi semplicemente avere i vantaggi della radice aggregata qui senza i problemi trattando il servizio come un co-testo mini-limitato.

Questo ha uno svantaggio, ovviamente. Non puoi semplicemente RPC (webservice, SOAP o REST) ​​dentro e fuori dai servizi e tra di loro o ottieni un antipattern SOA chiamato 'il nodo' a causa dell'accoppiamento temporale. Devi usare l'inversione del modello di comunicazione, noto anche come "Pub-Sub", che piace solo ai gestori di eventi e ai predecessori di eventi, ma (1) tra i processi (che puoi mettere su macchine separate se vengono sovraccaricate su una).

il vero problema è che non si desidera che un servizio abbia bisogno di ottenere i dati da un altro servizio per "bloccare" o attendere - è necessario attivare e dimenticare il messaggio e lasciare che un gestore altrove nel programma lo raccolga per completare l'elaborazione. Ciò significa che devi fare la tua logica in modo diverso. nServicebus automatizza il modello 'saga' per aiutare con questo, ma alla fine devi sviluppare un diverso stile di codifica. Puoi ancora fare tutto, devi solo farlo diversamente!

Il libro "SOA Patterns" di Arnon Rotem-Gal-Oz risponde a molte domande al riguardo. Compreso l'uso del "modello di servizio attivo" per replicare periodicamente i dati da servizi esterni ai propri quando si presenta la necessità (sarebbero stati necessari molti RPC o il collegamento non è affidabile / non nell'ecosistema di pubblicazione / sottoscrizione).

Solo per anteprima, interfacce utente fanno devono RPC in servizi. I report vengono generati da un database di report alimentato dai database dei servizi. Alcuni dicono che non sono necessari rapporti e che il problema dovrebbe essere risolto in un altro modo. Sii scettico su quel discorso.

Alla fine, però, non tutte le cose possono essere correttamente classificate in un unico servizio. Il mondo non funziona con il codice ravioli! Quindi dovrai infrangere le regole. Anche se non dovresti mai farlo, i nuovi sviluppatori del progetto lo faranno quando lo lascerai. Ma non preoccuparti, se fai ciò che puoi, l'85% che segue le regole renderà un programma molto più gestibile.

Wow, è stato lungo.


Grazie per la risposta dettagliata, farò sicuramente qualche lettura su SOA.
krixon,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.