Większość wzorców projektowania taktycznego DDD należy do paradygmatu zorientowanego obiektowo, a model anemiczny opisuje sytuację, w której cała logika biznesowa jest umieszczana w usługach, a nie w obiektach, co czyni je rodzajem DTO. Innymi słowy model anemiczny jest synonimem stylu proceduralnego, który nie jest zalecany w przypadku modelu złożonego.
Nie mam dużego doświadczenia w czystym programowaniu funkcjonalnym, ale chciałbym wiedzieć, jak DDD pasuje do paradygmatu FP i czy w tym przypadku nadal istnieje termin „model anemiczny”.
Aktualizacja : Ostatnio opublikowane książki i wideo na ten temat.
functional-programming
domain-driven-design
Pavel Voronin
źródło
źródło
Odpowiedzi:
Sposób, w jaki opisany jest problem „modelu anemicznego”, nie przekłada się dobrze na FP. Najpierw należy go odpowiednio uogólnić. Sercem modelu anemicznego jest model, który zawiera wiedzę o tym, jak właściwie go używać, który nie jest zamknięty w samym modelu. Zamiast tego wiedza ta rozprzestrzenia się na stos powiązanych usług. Usługi te powinny być tylko klientami tego modelu, ale z powodu anemii ponoszą za to odpowiedzialność . Rozważmy na przykład
Account
klasę, której nie można użyć do aktywacji lub dezaktywacji kont, a nawet wyszukiwania informacji o koncie, chyba że są obsługiwane przezAccountManager
klasę. Konto powinno być odpowiedzialne za podstawowe operacje na nim, a nie niektóre zewnętrzne klasy menedżerów.W programowaniu funkcjonalnym podobny problem występuje, gdy typy danych nie odzwierciedlają dokładnie tego, co powinny modelować. Załóżmy, że musimy zdefiniować typ reprezentujący identyfikatory użytkownika. Definicja „anemiczna” oznaczałaby, że identyfikatory użytkowników są łańcuchami. Jest to technicznie wykonalne, ale napotyka ogromne problemy, ponieważ identyfikatory użytkowników nie są używane jak ciągi arbitralne. Nie ma sensu ich łączenia ani wycinania podciągów, Unicode nie powinien mieć większego znaczenia i powinny być łatwe do osadzenia w adresach URL i innych kontekstach o ściśle ograniczonych znakach i formatach.
Rozwiązanie tego problemu zwykle przebiega w kilku etapach. Prostym pierwszym cięciem jest powiedzenie: „Cóż, a
UserID
jest reprezentowane równorzędnie z łańcuchem, ale są to różne typy i nie można użyć jednego tam, gdzie oczekujesz drugiego”. Haskell (i niektóre inne wpisane języki funkcjonalne) udostępnia tę funkcję poprzeznewtype
:To definiuje
UserID
funkcję, która gdy daliString
konstruuje wartość, która jest traktowana jakUserID
przez system typu, ale jest nadal tylkoString
przy starcie. Teraz funkcje mogą zadeklarować, że wymagająUserID
zamiast ciągu znaków; używającUserID
s, gdzie wcześniej używałeś łańcuchów chroniących przed kodemUserID
łączącym dwa s razem. System typów gwarantuje, że to się nie stanie, nie są wymagane żadne testy.Słabością jest to, że kod może nadal brać dowolnej
String
jak"hello"
i zbudowaćUserID
z niego. Dalsze kroki obejmują utworzenie „inteligentnego konstruktora”, który po podaniu łańcucha sprawdza niektóre niezmienniki i zwraca tylko,UserID
jeśli są spełnione. Następnie „głupi”UserID
konstruktor zostaje ustawiony na prywatny, więc jeśli klient chceUserID
, musi użyć inteligentnego konstruktora, zapobiegając w ten sposób powstaniu zniekształconych identyfikatorów użytkowników.Nawet dalsze kroki definiują
UserID
typ danych w taki sposób, że niemożliwe jest zbudowanie takiego, który jest zniekształcony lub „niewłaściwy”, po prostu z definicji. Na przykład zdefiniowanieUserID
jako listy cyfr:Aby zbudować
UserID
listę cyfr, należy podać. Biorąc pod uwagę tę definicję, trywialne jest pokazanie, żeUserID
istnienie, którego nie można przedstawić w adresie URL, jest niemożliwe . Definiowanie modeli danych, jak to w Haskell jest często wspomagane przez zaawansowanych funkcji systemowych, takich jak typ Rodzaje danych i typy uogólnione dane (GADTs algebraiczne) , które pozwalają na system typu zdefiniować i udowodnić więcej niezmienników o kodzie. Gdy dane są oddzielone od zachowania, definicja danych jest jedynym sposobem wymuszenia zachowania.źródło
Niezmienność w dużym stopniu sprawia, że nie jest konieczne ścisłe łączenie funkcji z danymi, jak zaleca OOP. Możesz wykonać dowolną liczbę kopii, nawet tworząc pochodne struktury danych, w kodzie daleko odbiegającym od oryginalnego kodu, bez obawy, że oryginalna struktura danych nieoczekiwanie zmieni się spod ciebie.
Jednak lepszym sposobem na dokonanie tego porównania jest prawdopodobnie sprawdzenie, które funkcje przypisujesz do warstwy modelu w porównaniu z warstwą usług . Mimo, że nie wygląda tak samo jak w OOP, w FP często popełniany jest błąd polegający na wtłoczeniu wielu funkcji abstrakcji w jedną funkcję.
O ile mi wiadomo, nikt nie nazywa go modelem anemicznym, ponieważ jest to termin OOP, ale efekt jest taki sam. Możesz i powinieneś ponownie używać funkcji ogólnych tam, gdzie ma to zastosowanie, ale w przypadku bardziej złożonych lub specyficznych dla aplikacji operacji powinieneś również zapewnić bogaty zestaw funkcji tylko do pracy z twoim modelem. Tworzenie odpowiednich warstw abstrakcji to dobry projekt w każdym paradygmacie.
źródło
Podczas korzystania z DDD w OOP jednym z głównych powodów umieszczania logiki biznesowej w samych obiektach domeny jest to, że logika biznesowa jest zwykle stosowana przez mutowanie stanu obiektu. Jest to związane z enkapsulacją:
Employee.RaiseSalary
prawdopodobnie mutujesalary
poleEmployee
instancji, które nie powinno być publicznie ustawiane.W FP unika się mutacji, więc zaimplementowałbyś to zachowanie, tworząc
RaiseSalary
funkcję, która pobiera istniejąceEmployee
wystąpienie i zwraca noweEmployee
wystąpienie z nową pensją. Zatem nie jest wymagana żadna mutacja: tylko czytanie z oryginalnego obiektu i tworzenie nowego obiektu. Z tego powodu takaRaiseSalary
funkcja nie musi być zdefiniowana jako metoda wEmployee
klasie, ale może żyć w dowolnym miejscu.W takim przypadku naturalne staje się oddzielanie danych od zachowania: jedna struktura reprezentuje
Employee
dane jako (całkowicie anemiczne), podczas gdy jeden (lub kilka) modułów zawiera funkcje, które działają na tych danych (zachowując niezmienność).Zauważ, że łącząc dane i zachowanie, tak jak w DDD, zasadniczo naruszasz Zasadę Jednej Odpowiedzialności (SRP):
Employee
może być konieczna zmiana, jeśli zmienią się zasady dotyczące zmian wynagrodzeń; ale może również wymagać zmiany, jeśli zasady obliczania premii EOY ulegną zmianie. W przypadku podejścia oddzielonego od produkcji nie ma to miejsca, ponieważ możesz mieć kilka modułów, każdy z jedną odpowiedzialnością.Jak zwykle podejście FP zapewnia większą modułowość / kompozycyjność.
źródło
Myślę, że istotą sprawy jest to, że anemiczny model z całą logiką domen w usługach działających na tym modelu jest zasadniczo programowaniem proceduralnym - w przeciwieństwie do „prawdziwego” programowania OO, w którym masz obiekty „inteligentne” i zawierające nie tylko dane ale także logikę, która jest najbardziej związana z danymi.
Ten sam kontrast występuje z programowaniem funkcjonalnym: „rzeczywisty” FP oznacza używanie funkcji jako elementów pierwszej klasy, które są przekazywane jako parametry, a także budowane w locie i zwracane jako wartość zwracana. Ale kiedy nie wykorzystasz całej tej mocy i masz tylko funkcje działające na strukturach danych, które są między nimi przekazywane, to kończysz w tym samym miejscu: zasadniczo programujesz procedury.
źródło
Myślę, że tak, ale głównie jako taktyczne podejście do przechodzenia między niezmiennymi obiektami wartości lub jako sposób na uruchomienie metod na bytach. (Gdzie większość logiki wciąż żyje w bycie).
Cóż, jeśli masz na myśli „w sposób analogiczny do tradycyjnego OOP”, to pomaga zignorować zwykłe szczegóły implementacji i wrócić do podstaw: Jakiego języka używają Twoi eksperci w dziedzinie? Jakie masz zamiary przechwytywania od użytkowników?
Załóżmy, że rozmawiają o łączeniu procesów i funkcji razem, to wydaje się, że funkcje (a przynajmniej obiekty „do-er”) są w zasadzie twoimi domenami!
Zatem w tym scenariuszu „model anemiczny” prawdopodobnie wystąpiłby, gdy „funkcje” nie byłyby w rzeczywistości wykonywalne, a byłyby jedynie konstelacjami metadanych, które są interpretowane przez usługę, która naprawdę działa.
źródło