DDD - Esecuzione di operazioni semplici su aggregati complessi

2

Sto leggendo il libro blu DDD e c'è una cosa che non mi è ancora chiara e che è come eseguire in modo efficiente semplici operazioni su Aggregate che contengono molte Entità secondarie.

Un esempio

  • Considerando che ho un Order , (radice aggregata) con Line Item s.
  • Ogni Order può avere solo il 1000% diLine Item s. (Regola aziendale).

Passi

Per poter aggiungere una Line Item a Order dovrei:

  • Ricostituire Order con tutti è Line Item s dal suo repository.
    • orderRepo.getById(1)
  • aggiungi Line Item a Order ,
    • order.addLineItem(lineItem)
  • Salva nuovamente Order tramite il repository.
    • orderRepo.save(order)

A meno che mi manchi qualcosa, questo mi sembra terribilmente inefficiente. E se avessi il 900% diLine Items su quel Order ? Dovrei caricare molti dati dal DB per eseguire questa semplice operazione.

Sì, potrei caricare pigro il LineItems ma anche in questo caso non sarò in grado di controllare la regola aziendale che richiede che < 1000 Line Items deve esistere in un Order , a meno che non sia tutto Line Items sono caricati.

Inoltre, le implementazioni di caricamento lento mi colpiscono come un anti-pattern perché solitamente legano gli accessor alle proprietà con i repository.

    
posta Nik Kyriakides 28.07.2017 - 02:08
fonte

4 risposte

2

Quando si utilizza DDD, si compromettono le prestazioni per un modello di dominio migliore. E questa è un'area in cui questo compromesso potrebbe essere fatto.

Osservando il tuo problema, vorrei mettere in dubbio che LineItem dovrebbe essere parte di Order aggregato. Il punto di aggregazione è raggruppare le entità su cui sono eseguite più operazioni complesse. Immagina di avere un aggregato con entità interne e 10 operazioni. In un caso, 9 delle operazioni funzionano sulle entità. In tal caso, complicherebbe drasticamente la situazione se le entità non fossero sempre caricate. Se d'altra parte solo 1 delle operazioni ha bisogno di quelle entità, allora mi chiederei se quelle entità dovrebbero essere parte dell'aggregato. In caso di complessità delle operazioni, il tuo caso è relativamente semplice. Il conteggio degli oggetti è un'operazione banale, che può essere eseguita sul lato DB. Ma immagina se il limite fosse per il prezzo e non per il conteggio. In questo caso, devi eseguire una complessa logica di "calcolo del prezzo", che probabilmente richiederà tutti gli articoli dell'ordine con tutti i dati. Ciò significa che non puoi permetterti di non caricare elementi pubblicitari.

    
risposta data 28.07.2017 - 07:45
fonte
2

Credo che il tuo problema sia che pensi di aver bisogno di contenere% Item oggetti nel tuo aggregato Order . Questo è sbagliato. Se lo facessi, l'impronta della memoria sarebbe davvero folle e le prestazioni sarebbero terribili. E non dovresti farlo, perché una singola operazione (un comando) dovrebbe riguardare solo un aggregato. Ciò rende il tuo codice più coeso e conciso allo stesso tempo.

Per rimanere molto più performante e coerente, in realtà il LineItem è un oggetto molto semplice che contiene solo riferimenti a identificatori sia di Order sia di Item .

final class LineItem {

    private final OrderId orderId;

    private final ItemId itemId;

    public LineItem(OrderId orderId, ItemId itemId) {
        this.orderId = orderId;
        this.itemId  = itemId;
    }
}

e l'aggiunta di Item al tuo aggregato Order sarà modellata come segue:

public class Order {

    private static final int MAX_CAPACITY = 1000;

    private final OrderId orderId;

    private final List<LineItem> lineItems = new ArrayList<>();

    public void addItem(Item item) {
        if (lineItems.size() == MAX_CAPACITY) {
            // throw exception
        }

        lineItems.add(new LineItem(orderId, item.itemId()));
    }
}

Se ti stai chiedendo come leggere i dati da questo, la risposta è che non devi e di solito no. Gli aggregati possono crescere di dimensioni ma è a favore di mantenere le regole aziendali coerenti e visibili. Per letture e prestazioni è necessario un layer diverso che parli direttamente a un database, aggirando completamente gli aggregati. Puoi farlo perché sai che tutti i dati nel database HAS sono validi, perché tutte le modifiche sono apportate tramite aggregati che proteggono le regole.

Un'altra opzione per risolvere questo problema è avere GetItemsCount(OrderId) in OrderRepository , come altri hanno già suggerito. Tuttavia, credo che il tuo problema principale sia pensare a referenziare gli aggregati in modo errato.

Se stai cercando una buona fonte per la modellazione aggregata, guarda sicuramente Design efficace degli aggregati di Mr. Vaugh Vernon.

    
risposta data 28.07.2017 - 09:59
fonte
1
  1. Aggiungi un metodo al tuo repository chiamato GetOrderItemsCount() , passa l'ID dell'ordine a cui sei interessato e restituisci il numero di articoli attualmente nell'ordine. Questo soddisferà la regola della tua attività commerciale e potrai ottimizzarla nel modo desiderato.

  2. Non devi caricare tutti gli elementi esistenti in un ordine per aggiungerne uno nuovo, soprattutto se conosci già il conteggio corrente e lo hai verificato rispetto alla regola aziendale.

  3. Caricamento lento? Che carico pigro?

risposta data 28.07.2017 - 03:03
fonte
1

In DDD è necessario caricare l'intero aggregato dal repository prima di applicarvi un comando. Se ritieni di non aver bisogno di caricarlo completamente, il tuo limite Aggregato potrebbe essere sbagliato, potresti avere un aggregato troppo grande. Ricorda, gli aggregati sono il confine della transazione: ogni cosa che accade all'interno di un aggregato è al riparo da aggiornamenti simultanei, le operazioni sono strongmente coerenti. Questo safe-ness è bello ma costoso e, quindi, deve essere fatto secondo gli affari del mondo reale.

Also, lazy loading implementations strike me as an anti-pattern because they usually tie the property accessors with repositories.

Sì, nel contesto di progettazione Aggregato, il caricamento lento è un anti-pattern. Significa che il tuo limite Aggregato è sbagliato (molto probabile).

Each Order can only have 1000 Line Items. (Business Rule).

Questo è il massimo, ma devi considerare anche il caso medio in cui un order ha un conteggio medio del% in% di cosa, line item ?

Sì, per un grande 5 l'aggiunta di order sarà più lenta, ma quanto spesso accade?

Inoltre, non puoi proteggere questa regola aziendale interrogando il repository per il conteggio line item e confrontalo con il numero line item ! Questa operazione di query + controllo è coerente (è possibile aggiungere due elementi contemporaneamente nello stesso momento in cui ci sono 999 elementi nell'ordine)!

    
risposta data 28.07.2017 - 09:02
fonte

Leggi altre domande sui tag