Decyduję, czy powinienem użyć Rich Domain Model zamiast Anemic Domain Model i szukam dobrych przykładów tych dwóch.
Budowałem aplikacje internetowe przy użyciu Anemic Domain Model, wspieranego przez system warstwy Service -> Repository -> Storage , używając FluentValidation do walidacji BL i umieszczając wszystkie moje BL w warstwie usług.
Przeczytałem książkę DDD Erica Evana i on (wraz z Fowlerem i innymi) wydaje się myśleć, że Anemic Domain Models jest anty-wzorcem.
Tak więc naprawdę chciałem uzyskać wgląd w ten problem.
Poza tym naprawdę szukam dobrych (podstawowych) przykładów Rich Domain Model i korzyści w stosunku do Anemic Domain Model, który zapewnia.
Odpowiedzi:
Różnica polega na tym, że model anemiczny oddziela logikę od danych. Logika jest często umieszczane w zajęciach nazwanych
**Service
,**Util
,**Manager
,**Helper
i tak dalej. Te klasy implementują logikę interpretacji danych i dlatego przyjmują model danych jako argument. Na przykładpodczas gdy podejście z bogatą domeną odwraca to, umieszczając logikę interpretacji danych w bogatym modelu domeny. W ten sposób łączy logikę i dane, a bogaty model domeny wyglądałby następująco:
Ma to duży wpływ na spójność obiektu. Ponieważ logika interpretacji danych opakowuje dane (dostęp do danych można uzyskać tylko za pomocą metod obiektowych), metody mogą reagować na zmiany stanu innych danych -> To właśnie nazywamy zachowaniem.
W modelu anemicznym modele danych nie mogą zagwarantować, że są w stanie legalnym, podczas gdy w bogatym modelu domeny mogą. Model bogatej domeny stosuje zasady OO, takie jak hermetyzacja, ukrywanie informacji oraz łączenie danych i logiki, a zatem model anemiczny jest anty-wzorcem z perspektywy obiektu obiektowego.
Aby uzyskać głębszy wgląd, zajrzyj na mój blog https://www.link-intersystems.com/blog/2011/10/01/anemic-vs-rich-domain-models/
źródło
Bozhidar Bozhanov wydaje się opowiadać za modelem anemicznym w tym poście na blogu.
Oto podsumowanie, które przedstawia:
obiekty domeny nie powinny być zarządzane wiosną (IoC), nie powinny mieć DAO ani niczego związanego z infrastrukturą w nich wstrzykniętą
obiekty domeny mają obiekty domeny, od których zależą, ustawiane przez hibernację (lub mechanizm trwałości)
obiekty domeny wykonują logikę biznesową, zgodnie z ideą DDD, ale nie obejmuje to zapytań do bazy danych ani CRUD - tylko operacje na wewnętrznym stanie obiektu
rzadko są potrzebne DTO - obiekty domeny to w większości przypadków same DTO (co pozwala zaoszczędzić trochę kodu standardowego)
usługi wykonują operacje CRUD, wysyłają e-maile, koordynują obiekty domeny, generują raporty w oparciu o obiekty wielu domen, wykonują zapytania itp.
warstwa usług (aplikacji) nie jest tak cienka, ale nie zawiera reguł biznesowych, które są nieodłączne dla obiektów domeny
należy unikać generowania kodu. Abstrakcji, wzorców projektowych i DI należy używać, aby przezwyciężyć potrzebę generowania kodu, a ostatecznie - aby pozbyć się duplikowania kodu.
AKTUALIZACJA
Niedawno czytałem ten artykuł, w którym autorka opowiada się za podejściem hybrydowym - obiekty domenowe mogą odpowiadać na różne pytania wyłącznie na podstawie ich stanu (co w przypadku modeli całkowicie anemicznych prawdopodobnie odbyłoby się w warstwie usługowej)
źródło
Mój punkt widzenia jest taki:
Model domeny anemicznej = tabele bazy danych odwzorowane na obiekty (tylko wartości pól, brak rzeczywistego zachowania)
Model domeny bogatej = kolekcja obiektów, które ujawniają zachowanie
Jeśli chcesz stworzyć prostą aplikację CRUD, może wystarczy model anemiczny z klasycznym frameworkiem MVC. Ale jeśli chcesz zaimplementować jakąś logikę, model anemiczny oznacza, że nie będziesz robić programowania obiektowego.
* Zauważ, że zachowanie obiektu nie ma nic wspólnego z trwałością. Za utrwalanie obiektów domeny odpowiada inna warstwa (mapery danych, repozytoria itp.).
źródło
x
,y
,sum
idifference
. To cztery rzeczy. Możesz też argumentować, że to dodawanie i odejmowanie (dwie rzeczy). Możesz też argumentować, że to matematyka (jedno). Istnieje wiele postów na blogach o tym, jak znaleźć równowagę w stosowaniu SRP. Oto jeden: hackernoon.com/…Rysunek 1 przedstawia Anemic Domain Model, który jest w zasadzie schematem z pobierającymi i ustawiającymi.
W tym bogatszym modelu, zamiast po prostu ujawniać właściwości do odczytu i zapisu, publiczna powierzchnia Klienta jest utworzona z jawnych metod.
źródło
Address
, aleExtendedAddress
odziedziczyćAddress
z kilkoma dodatkowymi właściwościami? 2) Lub zmienićCustomerCreditCard
parametry konstruktora, które mają być przyjmowaneBankID
zamiastBankName
?Jedną z zalet bogatych klas domeny jest to, że możesz wywołać ich zachowanie (metody) za każdym razem, gdy masz odwołanie do obiektu w dowolnej warstwie. Ponadto masz tendencję do pisania małych i rozproszonych metod, które współpracują ze sobą. W klasach domeny anemicznej masz tendencję do pisania grubych metod proceduralnych (w warstwie usług), które są zwykle kierowane przez przypadek użycia. Zazwyczaj są trudniejsze w utrzymaniu w porównaniu z bogatymi klasami domen.
Przykład klas domeny z zachowaniami:
Metoda
needToDeliver()
zwróci listę elementów, które należy dostarczyć, w tym premię. Można go wywołać wewnątrz klasy, z innej pokrewnej klasy lub z innej warstwy. Na przykład, jeśli przejdzieszOrder
do przeglądania, możesz użyćneedToDeliver()
zaznaczonychOrder
do wyświetlenia listy pozycji do potwierdzenia przez użytkownika przed kliknięciem przycisku Zapisz, aby zachować plikOrder
.Odpowiadanie na komentarz
Oto jak używam klasy domeny z kontrolera:
Stworzenie
Order
iLineItem
to w jednej transakcji. Jeśli jednego zLineItem
nie można utworzyć, nieOrder
zostanie utworzony.Zwykle mam metodę, która reprezentuje pojedynczą transakcję, na przykład:
Wszystko w środku
deliver()
zostanie wykonane jako jedna transakcja. Gdybym potrzebował wykonać wiele niepowiązanych metod w jednej transakcji, utworzyłbym klasę usług.Aby uniknąć leniwego ładowania, używam nazwanego wykresu encji JPA 2.1. Na przykład w kontrolerze ekranu dostawy mogę utworzyć metodę ładowania
delivery
atrybutu i ignorowaniabonus
, taką jakrepository.findOrderByNumberFetchDelivery()
. Na ekranie bonusowym wywołuję inną metodę, która ładujebonus
atrybut i ignorujedelivery
, na przykładrepository.findOrderByNumberFetchBonus()
. To wymaga dyscypliny, ponieważ nadal nie mogę zadzwonićdeliver()
na ekranie bonusowym.źródło
Kiedy pisałem monolityczne aplikacje desktopowe, tworzyłem rozbudowane modele domen, które dawniej czerpałem radość z ich tworzenia.
Teraz piszę małe mikrousługi HTTP, jest jak najmniej kodu, w tym anemiczne DTO.
Myślę, że DDD i ten anemiczny argument pochodzą z ery monolitycznych aplikacji na komputery stacjonarne lub serwery. Pamiętam tamtą epokę i zgodzę się, że anemiczne modele są dziwne. Zbudowałem dużą, monolityczną aplikację do handlu walutami i nie było modelu, naprawdę, to było okropne.
W przypadku mikrousług małe usługi z ich bogatym zachowaniem są prawdopodobnie modelami i agregatami, które można komponować w ramach domeny. Dlatego same implementacje mikrousług mogą nie wymagać dalszego DDD. Aplikacja mikrousług może być domeną.
Mikrousługa zamówień może mieć bardzo mało funkcji, wyrażonych jako zasoby RESTful lub przez SOAP lub cokolwiek innego. Kod mikrousługi zamówień może być niezwykle prosty.
Większa, bardziej monolityczna pojedyncza (mikro) usługa, szczególnie taka, która utrzymuje model w pamięci RAM, może skorzystać na DDD.
źródło
Myślę, że źródłem problemu jest fałszywa dychotomia. Jak można wyodrębnić te 2 modele: bogaty i „anemiczny” i zestawić je ze sobą? Myślę, że jest to możliwe tylko wtedy, gdy masz błędne wyobrażenie o tym, czym jest klasa . Nie jestem pewien, ale wydaje mi się, że znalazłem to w jednym z filmów Bozhidara Bozhanova na Youtube. Klasa nie jest danymi + metodami na tych danych. Jest to całkowicie błędne rozumienie, które prowadzi do podziału klas na dwie kategorie: tylko dane, a więc model anemiczny i dane + metody - tak bogaty model (aby być bardziej poprawnym, istnieje trzecia kategoria: tylko metody).
Prawdą jest, że klasa jest pojęciem w pewnym modelu ontologicznym, słowem, definicją, terminem, ideą, to DENOTAT . I to zrozumienie eliminuje fałszywą dychotomię: nie można mieć TYLKO modelu anemicznego lub TYLKO bogatego modelu, ponieważ oznacza to, że twój model nie jest adekwatny, nie ma związku z rzeczywistością: niektóre pojęcia mają tylko dane, niektóre mają tylko metody, inne z nich są mieszane. Ponieważ staramy się opisać w tym przypadku pewne kategorie, zbiory obiektów, relacje, pojęcia z klasami, a jak wiemy, niektóre pojęcia są tylko procesami (metodami), niektóre z nich są tylko zbiorem atrybutów (danych), niektóre są to relacje z atrybutami (mieszane).
Uważam, że odpowiednia aplikacja powinna obejmować wszystkie rodzaje zajęć i unikać fanatycznego ograniczania się do jednego modelu. Bez względu na to, jak przedstawia się logika: za pomocą kodu lub z interpretowalnymi obiektami danych (takimi jak Free Monady ), w każdym razie: powinniśmy mieć klasy (pojęcia, denotaty) reprezentujące procesy, logikę, relacje, atrybuty, cechy, dane itp., A nie starać się unikać niektórych z nich lub sprowadzać wszystkie tylko do jednego rodzaju.
Możemy więc wyodrębnić logikę do innej klasy i pozostawić dane w oryginalnej, ale nie ma to sensu, ponieważ niektóre pojęcia mogą zawierać atrybuty i relacje / procesy / metody, a ich oddzielenie powieli pojęcie pod 2 nazwami, które mogą być zredukowane do wzorców: „OBJECT-Attributes” i „OBJECT-Logic”. Jest to dobre w językach proceduralnych i funkcjonalnych ze względu na ich ograniczenia, ale jest to nadmierne powściągliwość dla języka, który pozwala opisywać wszelkiego rodzaju koncepcje.
źródło
Modele domen anemicznych są ważne dla ORM i łatwego transferu przez sieci (podstawa wszystkich komercyjnych aplikacji), ale OO jest bardzo ważne dla hermetyzacji i uproszczenia części kodu „transakcyjnej / obsługi”.
Dlatego ważna jest umiejętność identyfikacji i konwersji z jednego świata do drugiego.
Nazwij modele Anemic coś w rodzaju AnemicUser lub UserDAO itp., Aby programiści wiedzieli, że istnieje lepsza klasa do użycia, a następnie przygotuj odpowiedniego konstruktora dla klasy none Anemic
i metodę adaptera, aby utworzyć klasę anemiczną na potrzeby transportu / trwałości
Staraj się używać żadnego anemicznego użytkownika wszędzie poza transportem / trwałością
źródło
Oto przykład, który może pomóc:
Anemiczny
Bez anemii
źródło
Klasyczne podejście do DDD nie stanowi, aby za wszelką cenę unikać modeli Anemic vs Rich. Jednak MDA może nadal stosować wszystkie koncepcje DDD (ograniczone konteksty, mapy kontekstowe, obiekty wartości itp.), Ale we wszystkich przypadkach używać modeli Anemic vs Rich. Istnieje wiele przypadków, w których korzystanie z usług domenowych do organizowania złożonych przypadków użycia domeny w zestawie agregatów domen jest znacznie lepszym podejściem niż tylko wywoływanie agregatów z warstwy aplikacji. Jedyną różnicą w stosunku do klasycznego podejścia DDD jest to, gdzie znajdują się wszystkie walidacje i reguły biznesowe? Istnieje nowa konstrukcja znana jako walidatory modeli. Walidatory zapewniają integralność pełnego modelu wejściowego przed każdym przypadkiem użycia lub przepływem pracy w domenie. Zagregowane jednostki główne i podrzędne są anemiczne, ale każda z nich może mieć własne walidatory modelu wywoływane w razie potrzeby, przez jego główny walidator. Walidatory nadal są zgodne z SRP, są łatwe w utrzymaniu i można je testować jednostkowo.
Przyczyną tej zmiany jest to, że teraz zmierzamy bardziej w kierunku najpierw interfejsu API niż pierwszego podejścia UX do mikrousług. REST odegrał w tym bardzo ważną rolę. Tradycyjne podejście API (ze względu na SOAP) zostało początkowo naprawione na podstawie poleceń API w porównaniu z czasownikami HTTP (POST, PUT, PATCH, GET i DELETE). Interfejs API oparty na poleceniach dobrze pasuje do podejścia obiektowego Rich Model i nadal jest bardzo ważny. Jednak proste interfejsy API oparte na CRUD, chociaż mogą pasować do modelu bogatego, są znacznie lepiej dostosowane do prostych modeli anemicznych, walidatorów i usług domenowych, aby zaaranżować resztę.
Uwielbiam DDD we wszystkim, co ma do zaoferowania, ale przychodzi czas, gdy trzeba go trochę rozciągnąć, aby pasował do ciągle zmieniającego się i lepszego podejścia do architektury.
źródło