2009-03-04 16 views
20

Przeczytałem niektóre z pytań dotyczących anemicznych modeli domen i separacji problemów. Jakie są najlepsze techniki wykonywania/dołączania logiki domeny do obiektów z anemicznymi domenami? W mojej pracy mamy dość anemiczny model i obecnie używamy klas "pomocnika" do wykonania logiki bazy danych/biznesowej na obiektach domeny. Na przykład:Techniki radzenia sobie z anemicznym modelem domeny

public class Customer 
{ 
    public string Name {get;set;} 
    public string Address {get;set;} 
} 

public class Product 
{ 
    public string Name {get;set;} 
    public decimal Price {get;set;} 
} 

public class StoreHelper 
{ 
    public void PurchaseProduct(Customer c, Product p) 
    { 
     // Lookup Customer and Product in db 
     // Create records for purchase 
     // etc. 
    } 
} 

Gdy aplikacja potrzebuje do zrobienia zakupu, to stworzyć StoreHelper i wywołać metodę na obiektach domen. Dla mnie sensowne byłoby, aby Klient/Produkt wiedział, jak zapisać się do repozytorium, ale prawdopodobnie nie chciałbyś użyć metod Save() na obiektach domeny. Ma to również sens w przypadku metody takiej jak Customer.Purchase (Product), ale polega to na wprowadzeniu logiki domeny do encji.

Oto niektóre techniki Natknąłem, nie wiem, które są dobre/złe:

  1. klienta i produktów dziedziczą z klasy „podmiot”, który zapewnia podstawowe operacje CRUD w sposób ogólny (użycie ORM).
    • Plusy: Każdy obiekt danych będzie automatycznie uzyskać operacje CRUD, ale są następnie przywiązany do bazy danych/ORM
    • Minusy: To nie rozwiązuje problemu działalności gospodarczej na obiektach, a także łączy wszystkie obiekty domeny do jednostki bazowej, która może nie być odpowiedni
  2. klas Zastosowanie pomocniczy do obsługi operacji CRUD i logikę biznesową
    • Czy jest sens mieć DAOs dla „czystych baz danych” operacji, oddzielna pomocnicy biznesu dla nich czy operacje związane z biznesem?
    • Czy w tym celu lepiej używać niestatycznych lub statycznych klas pomocniczych?
    • Plusy: obiekty domen nie są przywiązane do dowolnej bazy danych/logiki biznesowej (całkowicie anemiczny)
    • Wady: niezbyt OO, niezbyt naturalny używać pomocników w kodzie aplikacji (wygląda jak kod C)
  3. użyj techniki podwójnego wysyłki, w którym jednostka ma metody, aby zapisać się do dowolnego repozytorium
    • Plusy: lepsza separacja obawy
    • Wady: podmioty jakieś dodatkowe logiki załączony (chociaż jest oddzielona)
  4. W języku C# 3.0, można użyć metody rozszerzenie dołączyć metody CRUD/biznesowych do obiektu domeny bez dotykania
    • Czy to ważne podejście? Jakie są zalety/wady?
  5. Inne techniki?

Jakie są najlepsze techniki radzenia sobie z tym? Jestem całkiem nowy w DDD (czytam książkę Evansa - więc może to otworzy mi oczy)

Odpowiedz

7

Martin Fowler napisał wiele na temat modeli domen, w tym anemic domain models. Ma także krótkie opisy (i diagramy klas UML) wielu wzorców projektowych dla modeli domen i baz danych, które mogą być pomocne: Catalog of "Patterns of Enterprise Application Architecture".

Proponuję przejrzeć wzory Active Record i Data Mapper. Z opisu problemu wynika, że ​​klasy pomocnicze zawierają zarówno szczegóły dotyczące implementacji bazy danych, jak i domeny i.

Aktywny rekord przeniesie logikę domeny pomocnika i kod bazy danych do innych obiektów domeny (takich jak Twoja klasa bazowa Entity). Program odwzorowujący dane przenosi logikę domeny pomocnika do obiektów domeny i kodu bazy danych na osobny obiekt mapy. Obie metody byłyby bardziej zorientowane obiektowo niż klasowe klasy pomocnicze.

Książka Eric Evans "Domain Driven Design" jest znakomita. Robi się trochę sucho, ale zdecydowanie warto. InfoQ ma "Domain Driven Design Quickly" mini-book, który jest dobrym wprowadzeniem do książki Evansa. Plus "Domain Driven Design Quickly" jest dostępny jako bezpłatny PDF.

2

Zawsze myślałem o anemicznym modelu domeny jako anty wzorca.To jasne, że klient zakupi produkty, które mogą być zdolność generised przez implementację interfejsu

Interface IPurchase 
     Purchase(Product); 

, więc dowolnych obiektów domeny można następnie wdrożyć, że w razie potrzeby. W ten sposób możesz wprowadzić funkcjonalność obiektów swojej domeny - dokładnie tam, gdzie powinna być.

14

W celu uniknięcia anemiczny model byłaby swoje zajęcia pomocnicze:

Logic jak:
"Customer.PurchaseProduct (produkt Produkt, wypłata płatności)"
„Customer.KillCustomer (osoba zabójca, broń broń) "
powinien istnieć bezpośrednio w obiekcie domeny" Klient ".

Logic jak:
"Customer.IsCustomerAlive()"
"Customer.IsCustomerHappy()"
powinien udać się do specyfikacji.

Logic jak:
"Customer.Create()",
"Customer.Update()"
oczywiście powinien udać się do repozytoriów.

Logic jak:
"Customer.SerializeInXml()"
"Customer.GetSerializedCustomerSizeInBytes()"
powinien udać się do usług.

Złożonych konstruktorów należy udać się do fabryk.

Tak to widzę. Byłbym szczęśliwy, gdyby ktoś mógł skomentować moje rozumienie podejścia DDD.


Edit:

Czasami - model domeny anemiczne shouldn't be avoided.

Zmieniono moją odpowiedź, aby dodać, że DDD nie polega na zbieraniu i upuszczaniu wzorów.
DDD to sposób, w jaki myślimy.

+0

Wydaje się, że wiele różnych klas służy tylko klientowi. Dlaczego nie wyrzucić większości z nich w jednej klasie, z usługą do obsługi wszystkiego, co złożone? –

+0

Moja odpowiedź jest stara jak diabli. : D –

+0

@LuckyLindy Głównie dlatego, że DDD polega na tworzeniu pomostu pomiędzy ekspertami w dziedzinie i programistami. Model domeny nie powinien zawierać elementów technicznych, inaczej wszechobecny język nie będzie istniał. Aby wyprowadzić sprawy techniczne - musimy je streścić. Abstrahowanie od czegoś zawsze powoduje zawyżenie bazy kodu. –

0

Jednym ze sposobów, o których nie wspomniałeś, jest użycie AOP do obsługi dostępu do danych. Przykładem mojego niedawnego zastosowania tego podejścia (chociaż znacznie uproszczonego do celów publikacji) było to, że posiadałem domenę o nazwie Account, która miała metodę debit, zawierającą logikę biznesową wymaganą do skutecznego obciążenia rachunku.

N.B. Wszystko jest kod Java z notacją AspectJ AOP ...

public boolean debit(int amount) { 
    if (balance - amount >= 0) { 
     balance = balance - amount; 
     return true; 
    } 
    return false; 
} 

Przy odpowiednim repozytorium wtryskiwanego do mojego aspekcie I następnie wykorzystywane punkt przekroju do przechwytywania połączeń do tej metody ...

pointcut debit(Account account,int amount) : 
    execution(boolean Account.debit(int)) && 
    args(amount) && 
    target(account); 

. ..i zastosowano kilka rad:

after(Account account, int amount) returning (boolean result) : debit(account,amount) { 
    if (result) getAccountRepository().debit(account, amount); 
} 

moim zdaniem to daje ładny separacji obawy, i pozwala podmioty domeny skupić się wyłącznie na logice biznesowej y nasza aplikacja.

Powiązane problemy