2010-05-24 8 views
26

Stiamo seguendo Domain Driven Design per l'implementazione di un sito web di grandi dimensioni.Come evitare di avere oggetti molto grandi con Domain Driven Design

Tuttavia mettendo il comportamento sugli oggetti del dominio ci ritroviamo con classi molto grandi.

Ad esempio sul nostro oggetto WebsiteUser, abbiamo molti metodi diversi, ad es. gestione di password, cronologia ordini, rimborsi, segmentazione della clientela. Tutti questi metodi sono direttamente correlati all'utente. Molti di questi metodi delegano internamente a un altro oggetto figlio ma
ciò comporta ancora alcune classi molto grandi.

Sono impaziente di evitare di esporre molti oggetti figlio ad es. user.getOrderHistory(). getLatestOrder().

Quali altre strategie possono essere utilizzate per evitare questi problemi?

risposta

2

Mi sono imbattuto nello stesso problema e ho scoperto che utilizzare gli oggetti "manager" secondari era la soluzione migliore nel nostro caso.

Per esempio, nel tuo caso, si potrebbe avere:

User u = ...; 
OrderHistoryManager histMan = user.getOrderHistoryManager(); 

quindi è possibile utilizzare il histMan per tutto quello che vuoi. Ovviamente ci hai pensato, ma non so perché vuoi evitarlo. Separa le preoccupazioni quando si hanno oggetti che sembrano fare troppo.

Pensaci in questo modo. Se possedevi un oggetto "Umano" e dovevi implementare il metodo chew(). Lo inseriresti nell'oggetto Human o nell'oggetto figlio Mouth.

19

I problemi che si verificano non sono causati da Domain Driven Design, ma piuttosto da una mancanza di separazione delle preoccupazioni. Domain Driven Design non si limita a mettere insieme dati e comportamenti.

La prima cosa che consiglierei è di prendere un giorno o più e leggere Domain Driven Design Quickly disponibile come download gratuito da Info-Q. Ciò fornirà una panoramica dei diversi tipi di oggetti di dominio: entità, oggetti valore, servizi, archivi e fabbriche.

La seconda cosa che consiglierei è di andare a leggere su Single Responsibility Principle.

La terza cosa che consiglierei è di iniziare a immergersi in Test Driven Development. Mentre imparare a progettare scrivendo i test per primo non ti rende necessariamente un design eccezionale, tende a guidarti verso design liberamente accoppiati ea rivelare problemi di progettazione in precedenza.

Nell'esempio che hai fornito, WebsiteUser ha decisamente troppe responsabilità. In effetti, potresti non aver bisogno del WebsiteUser poiché gli utenti sono generalmente rappresentati da un ISecurityPrincipal.

È un po 'difficile suggerire esattamente come dovresti avvicinarti al tuo progetto data la mancanza di contesto di business, ma prima raccomanderei di fare un po' di brainstorming creando alcune schede che rappresentano ciascuno dei principali nomi che hai nel tuo sistema (ad es. Cliente, Ordine, Ricevuta, Prodotto, ecc.). Scrivi in ​​alto i nomi delle classi di candidati, quali sono le responsabilità che ritieni siano inerenti alla classe a sinistra e le classi a cui collaborerà a destra. Se alcuni comportamenti non sembrano appartenere ad alcuno degli oggetti, è probabilmente un buon candidato al servizio (ad esempio AuthenticationService).Distribuisci le carte sul tavolo con i tuoi college e discuti. Non fare troppo di questo però, dato che questo è in realtà solo un esercizio di progettazione di brainstorming. Può essere un po 'più facile farlo a volte che usare una lavagna perché puoi spostare le cose.

A lungo termine, dovresti davvero prendere il libro Domain Driven Design di Eric Evans. È una lettura grande, ma vale la pena. Ti suggerisco inoltre di scegliere tra Agile Software Development, Principles, Patterns, and Practices o Agile Principles, Patterns, and Practices in C# a seconda delle tue preferenze di lingua.

+0

Grazie per questo commento. Ho letto il libro di Mr. Evans. Immagino che il problema sia quando un'entità ha molti collaboratori. per esempio. un utente del sito Web (o principale) ha un ultimo ordine, un primo ordine, un carrello della spesa, una password ecc. Tutti questi sono direttamente correlati a un utente del sito web. – Pablojim

+1

L'autenticazione, lo stato dell'ordine in attesa (ad esempio il carrello acquisti) e la cronologia degli ordini sono preoccupazioni distinte. Prendi in considerazione l'estrazione dell'autenticazione in un IAuthenticationService e la creazione di un IOrderHistoryRepository (o forse un IOrderHistoryService) per incapsulare il comportamento del recupero della cronologia degli ordini per un determinato utente. –

2

Una semplice regola empirica da seguire è "la maggior parte dei metodi della tua classe DEVONO utilizzare la maggior parte delle variabili di istanza nella tua classe" - se segui questa regola le classi saranno automaticamente della giusta dimensione.

+0

Questa è un'osservazione interessante. Potete fornire qualche link? – ya23

+0

Fondamentalmente è un modo più semplice per ricordare SRP: l'ho letto in uno dei libri oo, ma l'ho trovato davvero interessante e mi è rimasto in mente. – OpenSource

+0

questo è anche noto come alta coesione – beluchin

2

Si consiglia di prendere in considerazione l'inversione di alcune cose. Ad esempio, un Cliente non ha bisogno di avere una proprietà dell'Ordine (o una cronologia degli ordini) - puoi lasciare quelli fuori dalla classe Cliente. Così, invece di

 
public void doSomethingWithOrders(Customer customer, Calendar from, Calendar to) { 
    List = customer.getOrders(from, to); 
    for (Order order : orders) { 
     order.doSomething(); 
    } 
} 

si potrebbe invece fare:

 
public void doSomethingWithOrders(Customer customer, Calendar from, Calendar to) { 
    List = orderService.getOrders(customer, from, to); 
    for (Order order : orders) { 
     order.doSomething(); 
    } 
} 

Questo è l'accoppiamento 'perdente', ma ancora è possibile ottenere tutti gli ordini che appartengono a un cliente. Sono sicuro che ci sono persone più intelligenti di me che hanno i nomi e i collegamenti giusti che si riferiscono a quanto sopra.

9

Anche se gli umani reali hanno molte responsabilità, ti stai dirigendo verso lo God object anti-pattern.

Come altri hanno già accennato, si dovrebbe estrarre queste responsabilità in separati Repositories e/o Servizi di dominio . Es .:

SecurityService.Authenticate(credentials, customer) 
OrderRepository.GetOrderHistoryFor(Customer) 
RefundsService.StartRefundProcess(order) 

essere specifico con le convenzioni di denominazione (vale a dire utilizzare OrderRepository o OrderService, invece di OrderManager)

Hai esegue in questo problema a causa di convenienza. Ad esempio, è conveniente trattare una come radice aggregata e accedere a tutto attraverso di essa.

Se si inserisce più enfasi sulla chiarezza invece di convenienza, Dovrebbe aiutare separare queste preoccupazioni. Sfortunatamente, significa che i membri del team devono ora essere a conoscenza dei nuovi servizi.

Un altro modo di pensare ad esso: proprio come Entità non devono svolgere la propria persistenza (che è il motivo per cui usiamo repository), i tuoi WebsiteUser non dovrebbe gestire Rimborsi/Segmentazione/etc.

Spero che questo aiuti!

1

Credo che il tuo problema sia in realtà correlato ai contesti limitati.Per quello che vedo, "che riguarda password, cronologia ordini, rimborsi, segmentazione clienti", ognuno di questi può essere un contesto limitato. Pertanto, potresti prendere in considerazione la possibilità di suddividere il tuo utente del sito Web in più entità, ciascuna corrispondente a un contesto. Potrebbero esserci delle duplicazioni, ma ti concentri sul tuo dominio e ti libererai di classi molto grandi con molteplici responsabilità.

+0

+1 Questo è abbastanza vicino a quello che sarebbe DDD con DCI. Separa il sistema da ciò che fa il sistema. – Gordon

Problemi correlati