Evitare oggetti di dominio gonfiati

12

Stiamo provando a spostare i dati dal nostro livello di servizio gonfiato nel nostro livello Dominio utilizzando un approccio DDD. Al momento disponiamo di molta logica aziendale nei nostri servizi, che è distribuita ovunque e non beneficia dell'eredità.

Abbiamo una classe Dominio centrale che è al centro della maggior parte del nostro lavoro: un commercio. L'oggetto Trade saprà come valutare se stesso, come stimare il rischio, convalidare se stesso, ecc. Possiamo quindi sostituire i condizionali con il polimorfismo. Ad esempio: SimpleTrade si auto-quoterà da sola, ma ComplexTrade si farà un altro prezzo.

Tuttavia, siamo preoccupati che questo aumenterà le classi del commercio. Dovrebbe davvero essere responsabile della propria elaborazione, ma la dimensione della classe aumenterà in modo esponenziale man mano che vengono aggiunte ulteriori funzionalità.

Quindi abbiamo delle scelte:

  1. Inserisci la logica di elaborazione in Trade class. La logica di elaborazione è ora polimorfica in base al tipo di operazione, ma la classe commerciale ora ha più responsabilità (prezzo, rischio, ecc.) Ed è grande
  2. Inserisci la logica di elaborazione in un'altra classe come TradePricingService. Non è più polimorfico con l'albero di ereditarietà commerciale, ma le classi sono più piccole e più facili da testare.

Quale sarebbe l'approccio suggerito?

    
posta djcredo 22.11.2011 - 12:35
fonte

4 risposte

8

Se stai andando su Domain Driven, considera la tua classe Trade come una radice aggregata e suddivide le sue responsabilità in altre classi.

Non vuoi finire con una sottoclasse di Trade per ogni combinazione di prezzo e rischio, quindi una Trade può contenere oggetti Price e Risk (composizione). Gli oggetti Price e Risk eseguono i calcoli effettivi, ma non sono visibili a nessuna classe tranne Trade. Puoi ridurre le dimensioni di Trade, senza esporre le tue nuove classi al mondo esterno.

Prova ad usare la composizione per evitare grandi alberi ereditari. Troppa eredità può portare a situazioni in cui si tenta di calzare un comportamento che non si adatta veramente al modello. È meglio estrapolare quelle responsabilità in una nuova classe.

    
risposta data 22.11.2011 - 20:48
fonte
4

La tua domanda mi fa sicuramente pensare al Pattern di strategia . Quindi puoi scambiare varie strategie di trading / prezzi, in modo simile a ciò che stai chiamando a TradePricingService .

Sicuramente penso che il consiglio che otterrai qui sarà utilizzare la composizione anziché l'ereditarietà.

    
risposta data 23.11.2011 - 00:56
fonte
2

Una possibile soluzione che ho usato in un caso simile è il schema di progettazione dell'adattatore (la pagina di riferimento contiene molte codice di esempio). Forse in combinazione con il schema di progettazione della delega per un facile accesso ai metodi principali.

Fondamentalmente, si divide la funzionalità Trader in un numero di aree disgiunte - ad es. gestione di prezzi, rischi, convalida tutti potrebbero essere aree diverse. Per ogni area, è quindi possibile implementare una gerarchia di classi separata che gestisce tale funzionalità esatta nelle diverse varianti necessarie, ovvero un'interfaccia comune per ciascuna area. La classe Trader principale viene quindi ridotta ai dati di base e ai riferimenti a un numero di oggetti del gestore, che possono essere costruiti quando necessario. Come

interface IPriceCalculator {
  double getPrice(ITrader t);
}
interface ITrader {
  IPriceCalculator getPriceCalculator();
}
class Tracer implements ITrader {
  private IPriceCalculator myPriceCalculator = null;
  IPriceCalculator getPriceCalculator() {
    if (myPriceCalculator == null)
      myPriceCalculator = PriceCalculatorFactory.get(this);
    return myPriceCalculator;
  }
}

Uno dei principali vantaggi di questo approccio è che le possibili combinazioni di ad es. i prezzi e le rotture sono completamente separati e possono quindi essere combinati secondo necessità. Questo è piuttosto difficile con l'ereditarietà a thread singolo della maggior parte dei linguaggi di programmazione. La decisione su quale combinazione utilizzare può essere calcolata anche molto tardi: -)

Di solito cerco di mantenere le classi dell'adattatore, ad es. le sottoclassi di IPriceCalculator sopra - stateless. Cioè queste classi non dovrebbero contenere dati locali se possibile, per ridurre il numero di istanze che devono essere create. Quindi di solito fornisco l'argomento principale adattato come argomento in tutti i metodi, come in getPrice(ITrader) sopra.

    
risposta data 22.11.2011 - 14:11
fonte
2

non può dire molto sul tuo dominio, ma

We have a central Domain class which is the focus of most of our work - a Trade.

... è un odore per me. Probabilmente cercherò di delineare le diverse responsabilità della classe e eventualmente scomporla a diversi aggregati. Gli aggregati sarebbero quindi progettati attorno a ruoli e / o punti di vista degli stakeholder / esperti di dominio coinvolti. Se Price e Risk sono coinvolti nello stesso comportamento / caso d'uso, probabilmente appartengono allo stesso aggregato. Ma se sono disaccoppiati forse appartengono a aggregati separati.

Forse RiskEvalutazione potrebbe essere un'entità separata nel tuo dominio, eventualmente con uno specifico ciclo di vita (non posso davvero dire, sto solo speculando ... tu conosci il tuo dominio, io non lo so ), ma la chiave è rendere espliciti i concetti impliciti e evitare l'accoppiamento non guidato dal comportamento, ma solo dall'accoppiamento dei dati legacy.

In generale, penserei al comportamento previsto e ai diversi cicli di vita dei componenti coinvolti. Solo l'aggiunta di comportamenti in cima ai dati raggruppati crea oggetti gonfiati. Ma i dati sono stati raggruppati in base a un progetto basato sui dati esistente, quindi non è necessario attenersi a questo.

    
risposta data 23.11.2011 - 13:23
fonte

Leggi altre domande sui tag