Chciałbym wiedzieć, jakie są wady i zalety korzystania z modelu domeny anemicznej (patrz link poniżej).
Chciałbym wiedzieć, jakie są wady i zalety korzystania z modelu domeny anemicznej (patrz link poniżej).
Odpowiedzi:
Profesjonaliści:
Wady:
Skoro „Anemic Domain Model” jest anty-wzorcem, dlaczego jest tak wiele systemów, które to implementują?
Myślę, że jest kilka powodów
1. Złożoność systemu
W prostym systemie (czyli prawie wszystkie przykłady i przykładowy kod, które można znaleźć w Internecie), jeśli chcę zaimplementować:
Dodawanie produktu do zamówienia
Umieściłem tę funkcję w Zakonie
public void Order.AddOrderLine(Product product)
{
OrderLines.Add(new OrderLine(product));
}
Ładny i super obiektowy.
Teraz powiedzmy, że muszę się upewnić, że muszę sprawdzić, czy produkt istnieje w magazynie i zgłosić wyjątek, jeśli tak nie jest.
Naprawdę nie mogę już złożyć zamówienia na zamówienie, ponieważ nie chcę, aby moje zamówienie było zależne od zapasów, więc teraz musi przejść do usługi
public void OrderService.AddOrderLine(Order order, Product product)
{
if (!InventoryService.Has(product)
throw new AddProductException
order.AddOrderLine(product);
}
Mógłbym również przekazać IInventoryService do Order.AddOrderLine, co jest inną opcją, ale nadal uzależnia zamówienie od InventoryService.
W Order.AddOrderLine nadal jest pewna funkcjonalność, ale zwykle jest ona ograniczona do zakresu zamówienia, podczas gdy z mojego doświadczenia wynika o wiele więcej logiki biznesowej poza zakresem zamówienia.
Kiedy system jest czymś więcej niż tylko podstawowym CRUDem, większość swojej logiki skończy się w OrderService, a bardzo mało w Order.
2. Widok programisty na OOP
W Internecie toczy się wiele gorących dyskusji na temat tego, która logika powinna dotyczyć podmiotów.
Coś jak
Zamów, oszczędzaj
Czy Zakon powinien wiedzieć, jak się ratować, czy nie? Powiedzmy, że mamy do tego repozytoria.
Czy teraz zamówienie może dodawać linie zamówienia? Jeśli spróbuję to zrozumieć używając prostego angielskiego, to też nie ma sensu. Użytkownik dodaje produkt do zamówienia, więc powinniśmy zrobić User.AddOrderLineToOrder ()? To wydaje się przesada.
A co z OrderService.AddOrderLine (). Teraz to ma sens!
Moje rozumienie OOP jest takie, że w celu hermetyzacji umieszczasz funkcje w klasach, w których funkcja będzie musiała uzyskać dostęp do stanu wewnętrznego klasy. Jeśli chcę uzyskać dostęp do kolekcji Order.OrderLines, umieszczam Order.AddOrderLine () w polu Order. W ten sposób stan wewnętrzny klasy nie zostanie ujawniony.
3. Kontenery IoC
Systemy korzystające z kontenerów IoC są zwykle w pełni anemiczne.
Dzieje się tak, ponieważ możesz testować swoje usługi / repozytoria, które mają interfejsy, ale nie możesz testować obiektów domeny (łatwo), chyba że umieścisz interfejsy na wszystkich z nich.
Ponieważ "IoC" jest obecnie chwalone jako rozwiązanie wszystkich twoich problemów programistycznych, wiele osób ślepo je śledzi i w ten sposób uzyskuje Anemic Domain Modele.
4. OOP jest trudne, proceduralne łatwe
Mam trochę " Klątwy Wiedzy ", ale odkryłem, że dla nowszych programistów posiadanie DTO i usług jest o wiele łatwiejsze niż Rich Domain.
Być może dzieje się tak dlatego, że w przypadku Rich Domain trudniej jest określić, na których klasach należy umieścić logikę. Kiedy tworzyć nowe zajęcia? Których wzorców użyć? itp..
W przypadku usług bezstanowych wystarczy wcisnąć go w usługę o najbliższej nazwie.
Po tym od bardzo dawna w mojej głowie pojawiła się myśl. Jestem przekonany, że termin „OOP” nabrał znaczenia, które nie jest do niego przeznaczone. Anagram oznacza „programowanie obiektowe”, jak wszyscy dobrze wiemy. Skupiamy się oczywiście na słowie „zorientowany”. To nie jest „OMP”, co oznacza „programowanie z mandatem obiektowym”. Zarówno ADM, jak i RDM są przykładami OOP. Wykorzystują obiekty, właściwości, interfejsy metod i tak dalej. Istnieje jednak różnica między ADM i RDM w tym, jak wybieramy hermetyzację. To są dwie różne rzeczy. Stwierdzenie, że ADM jest zły OOP, nie jest dokładnym stwierdzeniem. Może zamiast tego potrzebujemy innych terminów dla różnych poziomów hermetyzacji. Poza tym nigdy nie lubiłem terminu anty-wzór. Zwykle jest przypisywany czemuś przez członków przeciwnej grupy. Zarówno ADM, jak i RDM są prawidłowym wzorcem, mają po prostu różne cele i mają na celu rozwiązanie różnych potrzeb biznesowych. Ci z nas, którzy praktykują DDD, powinni przynajmniej to docenić i nie spaść na poziom innych, atakując tych, którzy zdecydują się wdrożyć ADM. Tylko moje myśli.
„Jest to anty-wzorzec, więc inni programiści będą pytać, czy rozumiesz koncepcje projektowania zorientowanego obiektowo”.
„Anemiczny model domeny jest anty-wzorcem. Anemiczne wzorce nie mają zalet”.
To, czy anemiczny model domeny jest anty-wzorcem, jest kwestią opinii. Martin Fowler twierdzi, że tak, wielu programistów, którzy znają OO na wylot, twierdzi, że tak nie jest. Podawanie opinii jako faktu rzadko jest pomocne.
A nawet gdyby był powszechnie akceptowany jako anty-wzór, są szanse, że nadal miałby pewne (choć stosunkowo niewielkie) korzyści.
the chances are it would still have some (though relatively little) upside.
Więc proszę, wymień niektóre! Przynajmniej jeden, zamiast stawiać hipotezę!
Wydaje mi się, że głównym zarzutem Fowlera jest to, że ADM nie są OO w następującym sensie. Jeśli projektuje się system „od zera” wokół pasywnych struktur danych, którymi manipulują inne fragmenty kodu, to z pewnością pachnie to bardziej projektowaniem proceduralnym niż obiektowym.
Sugeruję, że istnieją co najmniej dwie siły, które mogą wytworzyć taki projekt:
Projektanci / programiści, którzy nadal myślą, że są proceduralnie wymagane do pracy w środowisku zorientowanym obiektowo (lub zakładają, że mogą ...) stworzyć nowy system, oraz
Programiści pracujący nad nadaniem „twarzy” usługi w starszym systemie zaprojektowanym w sposób inny niż OO (niezależnie od języka).
Gdyby na przykład budować zestaw usług w celu udostępnienia funkcjonalności istniejącej aplikacji mainframe COBOL, można by zdefiniować usługi i interfejsy w oparciu o model koncepcyjny, który nie odzwierciedla wewnętrznych struktur danych COBOL. Jeśli jednak usługa mapuje nowy model do starszych danych, aby użyć istniejącej, ale ukrytej implementacji, nowy model może być bardzo „anemiczny” w sensie artykułu Fowlera - np. Zestaw definicji w stylu TransferObject i relacje bez prawdziwego zachowania.
Ten rodzaj kompromisu może być bardzo powszechny w przypadku granic, przy których idealistycznie czyste systemy obiektowe muszą wchodzić w interakcję z istniejącym środowiskiem innym niż obiekt obiektowy.
Model domeny anemicznej (ADM) może być dobrym wyborem, jeśli Twój zespół nie jest w stanie lub nie chce zbudować bogatego modelu domeny (RDM) i utrzymywać go w czasie. Zwycięstwo z RDM wymaga szczególnej uwagi na dominujące abstrakcje używane w systemie. Wyobraź sobie, że w dowolnej grupie deweloperów nie więcej niż połowa i być może tylko jedna dziesiąta jej członków jest kompetentna w zakresie abstrakcji. O ile ta kadra (być może tylko jeden deweloper) nie jest w stanie utrzymać wpływu na działania całej grupy, RDM ulegnie entropii.
A entropia RDM boli w określony sposób. Jego twórcy wyciągną trudne lekcje. Na początku będą mogli sprostać oczekiwaniom swoich interesariuszy, ponieważ nie będą mieli historii, której mogliby sprostać. Ale gdy ich system stanie się bardziej skomplikowany (nie złożony) , stanie się kruchy; programiści będą próbowali ponownie wykorzystać kod, ale mają tendencję do wywoływania nowych błędów lub cofania się w rozwoju (i tym samym przekraczają swoje szacunki).
W przeciwieństwie do tego programiści ADM będą stawiać sobie niższe oczekiwania, ponieważ nie będą oczekiwać ponownego wykorzystania tak dużej ilości kodu dla nowych funkcji. Z biegiem czasu będą mieć system z wieloma niespójnościami, ale prawdopodobnie nie zepsuje się nieoczekiwanie. Ich czas wprowadzenia na rynek będzie dłuższy niż w przypadku udanego RDM, ale ich interesariusze raczej nie dostrzegą takiej możliwości.
„Programiści pracujący nad nadaniem usługowej„ twarzy ”starszemu systemowi zaprojektowanemu w sposób inny niż OO (niezależnie od języka).”
Jeśli myślisz o wielu aplikacjach LOB, te starsze systemy często nie będą używać tego samego modelu domeny, co Ty. Anemic Domain Model rozwiązuje ten problem za pomocą logiki biznesowej w klasach usług. Możesz umieścić cały ten kod interfejsu wewnątrz swojego modelu (w tradycyjnym sensie OO) - ale zazwyczaj kończy się to utratą modułowości.
Kiedy po raz pierwszy natknąłem się na artykuł Anemic Domain Model, pomyślałem: „O cholera, to właśnie robię. Wytrwałem i zastosowałem się do odniesień do książki Erica Evana, uważanej za dobry przykład, i pobrałem źródło. Okazuje się, że „niestosowanie anemicznego modelu domeny” nie oznacza „nieużywania klas usług, nieużywania mediatorów, nieużywania strategii” ani nawet „nakładania logiki na manipulowaną klasę”.
Przykłady DDD mają klasy usług, XyzUpdaters, singletony i IoC.
Jestem zdezorientowany tym, czym dokładnie jest model domeny anemicznej. Oczekuję, że „poznam to, kiedy to zobaczę”. Na razie jestem zadowolony z pozytywnego przykładu dobrego projektu.
Pracując z „dojrzałym” systemem z ADM i czuję, że mogę udzielić przynajmniej kilku anegdotycznych informacji zwrotnych na to pytanie.
1) Brak hermetyzacji
W systemie live z ADM istnieje możliwość zapisu np. 'Obj.x = 100; obj.save '', nawet jeśli narusza to logikę biznesową. Prowadzi to do szeregu błędów, które nie wystąpiłyby, gdyby niezmienniki były modelowane na obiekcie. Uważam, że to powaga i wszechobecność tych błędów jest najpoważniejszym negatywnym skutkiem dla ADM.
Uważam, że ważne jest, aby podkreślić, że w tym miejscu rozwiązanie funkcjonalne i rozwiązania proceduralne ADM znacznie się różnią, a wszelkie podobieństwa, które inni mogli wyciągnąć na powierzchnię podobieństw między ADM a rozwiązaniem funkcjonalnym, są przypadkowe.
2) Nadęcie kodu
Szacuję, że ilość kodu produkowanego w ADM jest 5-10 razy większa niż w przypadku rozwiązania OOP / RDM. Przyczyną tego jest być może 50% powtórzeń kodu, 30% to kod płyty głównej, a 20% to rozwiązywanie lub rozwiązywanie problemów wynikających z braku RDM.
3) Słabe zrozumienie problemów dziedzinowych
ADM i słabe zrozumienie problemów domeny idą w parze. Pojawiają się naiwne rozwiązania, słabo uwzględniane są wymagania ze względu na trudność w ich wsparciu przy pomocy istniejącego DM, a ADM staje się istotną barierą dla innowacji biznesowych, biorąc pod uwagę dłuższy czas rozwoju i brak elastyczności.
4) Trudność w utrzymaniu
Wymagany jest pewien poziom rygoru, aby zapewnić, że koncepcja domeny zostanie zmieniona we wszystkich miejscach, w których jest wyrażona, biorąc pod uwagę, że koncepcja nie może być tylko powtórną implementacją typu kopiuj i wklej. Często prowadzi to do tego, że te same błędy są badane i naprawiane przy wielu okazjach.
5) Zwiększona trudność przy wejściu na pokład
Myślę, że jedną z zalet RDM jest spójność koncepcji, które pozwalają na szybsze zrozumienie dziedziny. W przypadku ADM koncepcje mogą być fragmentaryczne i niejasne, przez co nowi deweloperzy mogą trudniej je zdobyć.
Kusiło mnie również, aby uwzględnić koszty wsparcia operacyjnego dla ADM, które są wyższe niż dla RDM, ale będzie to zależało od wielu czynników.
Jak zauważyli inni, spójrz na DDD (Greg Evans, Vince Vaughn i Scott Millett), aby poznać zalety RDM.
Jest to ten sam profesjonalista, co w przypadku większości anty-wzorców: pozwala na zajęcie wielu osób przez długi czas. Ponieważ menedżerowie zwykle zarabiają więcej, gdy zarządzają większą liczbą ludzi, istnieje silna zachęta, aby nie robić postępów.
Zgodnie z odpowiedzią Erica P, a także z tym, co napisali inni powyżej, wydaje się, że główną wadą ADM jest utrata OOD, w szczególności utrzymywanie logiki i danych koncepcji domeny razem, tak że szczegóły implementacji są ukryte podczas API może być bogate.
Eric zwraca uwagę, że często istnieją informacje spoza klasy domeny, które są niezbędne dla logiki działania na tej klasie, na przykład sprawdzanie zapasów przed dodaniem pozycji do zamówienia. Zastanawiam się jednak, czy odpowiedzią jest warstwa usług, która przechowuje tę nadrzędną logikę, czy też lepiej jest obsługiwana jako część projektu obiektu. Ktoś musi wiedzieć o obiekcie Inventory, obiekcie Product i obiekcie Order. Być może jest to po prostu obiekt OrderSystem, który ma element Inventory, listę zamówień i tak dalej. To nie będzie wyglądać inaczej niż usługa, ale myślę, że jest koncepcyjnie bardziej spójna.
Lub spójrz na to w ten sposób: Możesz mieć użytkownika z wewnętrznym saldem kredytowym i za każdym razem, gdy wywoływany jest User.addItemToOrder (item), pobiera on cenę przedmiotu i sprawdza kredyt przed jego dodaniem itd. To wydaje się rozsądne OO. projekt. Nie jestem pewien, co dokładnie stracono, zastępując to Service.addItemToUserOrder (użytkownik, przedmiot), ale nie jestem też pewien, co zyskałem. Wydaje mi się, że stratą byłaby dodatkowa warstwa kodu, a także bardziej skomplikowany styl pisania i wymuszona ignorancja leżącego u podstaw Modelu Domeny.
Należy zauważyć, że wraz ze wzrostem złożoności i ziarnistości zmienności systemów, hermetyzacja i konsolidacja punktów interfejsu zapewniana przez dobrze zaprojektowany model obiektowy przekazywania komunikatów sprawia, że zmiana i utrzymanie krytycznego kodu bez powszechnej refaktoryzacji jest znacznie bezpieczniejsza.
Warstwy usług stworzone przez ADM, choć z pewnością łatwiejsze do wdrożenia (ponieważ wymagają stosunkowo niewielkiego myślenia i mają wiele zdecentralizowanych punktów interfejsu), prawdopodobnie spowodują problemy w przyszłości, gdy nadejdzie czas na modyfikację działającego i rozwijającego się systemu.
Dodam też, że nie wszystkie przypadki w ogóle wymagają modelu domeny (nie mówiąc już o modelu ADM). Czasami lepiej jest użyć bardziej proceduralnego / funkcjonalnego stylu zadania, który jest oparty na danych i nie zależy od logiki / reguł biznesowych całej aplikacji.
Jeśli próbujesz zdecydować o zaletach i wadach całej aplikacji, myślę, że ważne jest, aby najpierw zaprojektować, jak każda z nich może wyglądać dla danej aplikacji, ZANIM zaczniesz pisać pojedynczą linię kodu. Po CRC lub oprawieniu aplikacji w ramy w obu stylach, cofnij się o krok i zdecyduj, który z nich jest bardziej sensowny i lepiej pasuje do aplikacji.
Zastanów się również z wyprzedzeniem, który z nich będzie łatwiejszy w utrzymaniu ...
Daje lepszą przewidywalność. Menedżerowie tacy jak to, zwłaszcza jeśli projekt jest płatny czas i materiały. Każda zmiana oznacza dużo pracy, więc trudna praca może być ukryta za wieloma powtarzalnymi pracami. W dobrze zaprojektowanym systemie DRY przewidywalność jest bardzo zła, ponieważ ciągle robisz nowe rzeczy.
Kiedy po raz pierwszy przeczytałem książkę Erica Evansa o projektowaniu opartym na domenach, nie rozumiałem, że nie jest to tylko zbiór taktycznych wzorców projektowania dobrych klas modeli domen.
Po tym, jak dowiedziałem się więcej na ten temat i wykorzystałem również strategiczne wzorce, w końcu zacząłem rozumieć, że na początku chodzi o głębokie zrozumienie problemów biznesowych , które próbujesz rozwiązać.
Dopiero potem można zdecydować, które części systemu będą nadawać się do stosowania wzorców taktycznych, takich jak agregaty, encje , repozytoria itp. Wraz z tzw. Bogatymi modelami domenowymi (w przeciwieństwie do anemicznych ). Jednak aby skorzystać z tych wzorców, logika biznesowa musi być wystarczająco złożona dla tej części systemu.
A więc przy wdrażaniu rozwiązania danego problemu należy najpierw ustalić, czy do tego konkretnego problemu lepiej podejść, stosując podejście oparte na CRUD, czy też inwestując w bogaty model dziedzinowy wraz ze wspomnianymi wzorcami taktycznymi.
Jeśli CRUD ma więcej sensu, np. Jeśli nie ma złożonej logiki biznesowej, a większość logiki dotyczy transformacji, przesyłania i utrwalania danych implementujących model domeny, może to być niepotrzebna przesada. Nie oznacza to, że nie będzie dużo pracy, ale po prostu to nie reguły biznesowe powodują najwięcej wysiłku wdrożeniowego. Ale w tym przypadku nie ma czegoś takiego jak anemiczny model domeny , po prostu dlatego, że w ogóle nie ma modelu domeny . Będziesz raczej widzieć takie rzeczy jak DTO (Data Transfer Objects) lub DAO(Data Access Objects) i klasy usług, które będą operować na danych. Odpowiednie operacje są w dużym stopniu związane z przekształcaniem danych z jednej reprezentacji do drugiej i przenoszeniem danych z bardzo małą lub prawie żadną logiką biznesową.
Jeśli ustaliłeś, że istnieje wiele złożonej logiki biznesowej, która również będzie się zmieniać w czasie, to inwestowanie w model domeny jest - z mojego doświadczenia - dobrym pomysłem. Powodem jest to, że łatwiej jest przedstawić perspektywę biznesową za pomocą kodu i ułatwić zrozumienie odpowiednich operacji, które odzwierciedlają domenę biznesową i jej reguły. Nie oznacza to, że w każdym przypadku użycia muszą istnieć klasy modelu domeny. Na przykład, jeśli nie ma stanu do zmutowania i utrwalenia, mogą istnieć tylko usługi domenowe, które zawierają logikę domeny, są zaimplementowane bardziej jak czyste funkcje.
Ale jeśli istnieje również stan, który ma być zmutowany i trwały, który ma również cel i znaczenie w domenie biznesowej, stan i zachowanie, które zmienia ten stan, powinny być zawarte . Dzięki temu nikt nie może tak łatwo obejść reguł biznesowych, co prowadzi do nieprawidłowych stanów i poważnych niepowodzeń. Źródłem takich problemów są często tzw. Anemiczne modele dziedzinowe . Dzieje się tak często, gdy widzisz kod, w którym różne komponenty działają na tej samej "anemicznej" klasie modelu domeny, sprawdzając jakąś część swojego stanu i zmieniając jakąś część swojego stanu bez dbania o ogólne niezmienniki tej jednostki biznesowej lub ich znajomości. Nie jest konieczne nazywanie tego anty-wzorcem, ale ważne jest, aby to zrozumiećtracisz wiele korzyści z bogatego modeli domen w podejście oparte na DDD wraz z wymienionymi problemami. Podczas korzystania z modelu domeny, w którym zachowanie i jego dane są umieszczone w tej samej klasie, może być również wiele różnych operacji wywołujących „klientów” tej klasy, ale nie muszą się przejmować, że niezmienniki biznesowe jednostki biznesowej są przestrzegane klasa modelu domeny zawsze zajmie się tym i może również informować „klienta” o nieprawidłowych operacjach lub nawet rzucać wyjątki jako sieć bezpieczeństwa.
Więc dolnej linii , myślę, że ważne jest, aby nie mylić danych strukturze jak klas (takie jak DTOs lub DAOs) z niedokrwistością klas modelu domeny . W starannie i celowo wybranym podejściu opartym na CRUD nie ma korzyści z próby użycia modelu domeny, ponieważ istnieje zbyt mniej złożona logika biznesowa.
Przez anemiczny modelu domeny chciałbym odwołać się do kodu, z którego widzę, że tam jest dużo złożonych logicznych reguł biznesowych i gospodarczych, które są rozproszone w różnych składników, które powinny raczej być zbliżona do tych danych logika się zmienia.
Jest też inna lekcja, której nauczyłem się po drodze: jeśli spróbujesz użyć tego samego języka biznesowego (zwanego również językiem wszechobecnym ) w swoim kodzie, którego używają stekowcy w swoim codziennym życiu zawodowym, zdobędziesz już wiele korzyści związanych domeny biznesowej i poprawie czytelności kodu, które pomogą Ci tak bardzo bez względu na to, czy używasz podejścia opartego na CRUD, czy opartym na modelu domeny.
Aby rozszerzyć odpowiedź Michaela, pomyślałbym, że jest (dość) jasne, gdzie ten kod powinien się znaleźć: do dedykowanego mediatora, który obsługuje interakcję między Zakonem a Inwentarzem.
Z mojego punktu widzenia kluczową kwestią w tej domenie jest to, że MUSI ona utrzymywać proste zachowanie testowe isInThisState()
metod itp. Z mojego doświadczenia wynika, że są one również rozproszone po łzach usług (sic :)) w większości firm i albo kopiowane, albo bez końca przepisywane. Wszystko to łamie standardowe zasady spójności.
Moim zdaniem podejście powinno polegać na dążeniu do DM, który zawiera tyle zachowań biznesowych, ile jest to praktyczne, a resztę należy umieścić w jasno wyznaczonych obszarach (tj. Nie w usługach)
Mój zespół osobiście preferuje ADM. mamy zestaw obiektów biznesowych, które reprezentują określone części naszej domeny. Korzystamy z usług, aby zapisać te obiekty do bazy danych. Nasze obiekty biznesowe mają metody, jednak te metody manipulują tylko ich stanem wewnętrznym.
Korzyści dla nas wynikające z używania ADM zamiast RDM można zobaczyć w sposobie utrwalania obiektów w db. Programiści pracujący na naszych starszych systemach kodu mogą używać naszych obiektów biznesowych (z nowego systemu) i nadal używać ich bieżącej warstwy dostępu do danych, aby utrwalić te obiekty w bazie danych. Korzystanie z RDM zmusiłoby programistów naszego dotychczasowego systemu do wprowadzenia obiektów repozytorium do naszego modelu biznesowego ... co byłoby niezgodne z ich obecną warstwą dostępu do danych.
Anemiczne modelu domeny jest anty-wzór. Anty-wzory nie mają zalet.