DDD - Radice aggregata con un numero elevato di bambini

6

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

Lavoro su un progetto che coinvolge i concetti di Conti e Transazioni (in senso finanziario). Un account può avere molte transazioni inserite contro di esso.

Mi sembra che Account e Transazione siano entrambe Entità e che l'Account sia una radice Aggregata contenente Transazioni poiché una Transazione non può esistere senza l'Account.

Tuttavia, quando vengo ad applicare questo codice, ho immediatamente riscontrato un problema. In molte situazioni non è particolarmente utile per me avere sempre un elenco di tutte le Transazioni in un Account. Mi interessa essere in grado di fare cose come calcolare il saldo del Conto e applicare invarianti come un limite di credito, ma voglio anche essere in grado di lavorare facilmente con un sottoinsieme di Transazioni (es. Visualizzare quelli che rientrano in un intervallo di date). / p>

In quest'ultimo caso, se usassi un TransactionRepository potrei accedere efficientemente solo agli oggetti di cui ho bisogno senza caricare l'intero elenco (potenzialmente molto grande). Tuttavia, ciò consentirebbe a cose diverse dall'account di funzionare con Transactions, il che significa che ho infranto il concetto di Account come radice aggregata.

In che modo le persone gestiscono questo tipo di situazione? Accetti solo le implicazioni di memoria e prestazioni del caricamento di un numero potenzialmente elevato di bambini per una radice aggregata?

    
posta krixon 23.02.2015 - 10:35
fonte

2 risposte

7

Suggerirei di stare attento con il "non può esistere senza la regola" . Questo parla del concetto di composizione nel design UML / OO e potrebbe essere stato uno degli approcci prescritti per progettare gli Aggregati nel libro blu originale DDD (non sono sicuro di ciò) ma è stato ampiamente rivisto da allora. Potrebbe essere un'idea migliore visualizzare le tue aggregazioni da una prospettiva perimetro di coerenza transazionale

.

L'idea è di non rendere i tuoi aggregati troppo grandi, dove potresti avere problemi di prestazioni come quello che fai notare, o troppo piccoli, perché alcuni invarianti si estendono inevitabilmente su più aggregati, causando problemi di blocco e di concorrenza aggregati. p>

La giusta dimensione di aggregazione 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 in modo che Transaction sia una radice aggregata a sé stante potrebbe essere la soluzione migliore.

    
risposta data 25.02.2015 - 12:22
fonte
6

tl; dr - rompi le regole se necessario. DDD non può risolvere tutti i problemi; infatti le idee oggetto che fornisce sono un buon consiglio e un buon inizio, ma scelte davvero sbagliate per alcuni problemi di business. Consideralo un suggerimento su come fare le cose.

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

Puoi risolverlo pigro caricando 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 Account possa usarli, non dovresti nemmeno archiviarli dove nessun altro sarebbe in grado di deserializzarli, come una tabella relazionale pubblica. Potresti averlo memorizzato con il "documento" dell'account in un DB del documento. In entrambi i casi, se qualcuno ha provato a sufficienza, potrebbero comunque vedere i dati. E 'lavorare' con esso. E quando non guardi, lo faranno!

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

Ciò che ti rendi veramente conto qui è che DDD e il puro uso del modello a oggetti ti portano 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 limiti.

Qualcuno potrebbe dire "non ottimizzare presto". Qui, in questo caso, però, si conosce la risposta: ci saranno abbastanza transazioni per impantanarsi in un metodo che le manterrà per sempre con l'account.

La vera risposta è iniziare a stare in piedi in SOA. Sul posto di lavoro abbiamo guardato i video di Udi Dahan "Distributed computing" e acquistato nServiceBus (solo la nostra scelta). Crea un servizio per gli account - con un proprio processo, code di messaggi, accesso a un database di relazioni che solo lui può vedere, e ... viola, potresti hardcode le istruzioni SQL nel programma e persino inserire un paio di script di transazione Cobol (scherzando naturalmente) ma seriamente ha più separazione delle preoccupazioni rispetto al più intelligente OO / Java snob potrebbe mai sognare.

Consiglierei comunque di modellarlo bene; puoi semplicemente avere i vantaggi della radice aggregata qui senza i problemi trattando il servizio come un countext con limiti minuscoli.

Questo ha un inconveniente, ovviamente. Non è possibile solo RPC (webservice, SOAP o REST) dentro e fuori i servizi e tra di loro o si ottiene un antipattern SOA chiamato 'il nodo' a causa dell'accoppiamento temporale. Devi usare l'inversione del pattern di comunicazione, noto anche come "Pub-Sub", che è simile ai gestori di eventi e agli eventi che generano eventi, ma (1) tra processi (che puoi mettere su macchine separate se si sovraccaricano su uno). / p>

il vero problema è che non vuoi un servizio che ha bisogno di ottenere dati da un altro servizio per "bloccare" o aspettare - devi licenziare e dimenticare il messaggio e lasciare che un gestore da qualche parte nel tuo programma lo prelevi per completare in lavorazione. Ciò significa che devi fare la tua logica in modo diverso. nServicebus automatizza il pattern 'saga' per aiutare con questo, ma alla fine, devi sviluppare uno stile di codifica diverso. Puoi ancora fare tutto, devi solo farlo in modo diverso!

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

Solo per l'anteprima, le UI fanno devono accedere a servizi RPC. I report sono generati da un database di reporting alimentato dai database dei servizi. Alcuni dicono che i rapporti non sono necessari 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 singolo servizio. Il mondo non funziona sul codice ravioli! Quindi dovrai violare le regole. Anche se non avresti mai dovuto farlo, i nuovi sviluppatori al progetto lo faranno quando lo lascerai. Ma non preoccuparti, se fai quello che puoi, l'85% che segue le regole renderà un programma molto più gestibile.

Wow, è stato lungo.

    
risposta data 25.02.2015 - 05:29
fonte

Leggi altre domande sui tag