Myślę że jest tutaj pewne nieporozumienie. Przede wszystkim to nie ma nic wspólnego z programowaniem funkcyjnym per se. Akurat trafiłeś na przykład takiego podejścia napisany funkcyjnie, ale równie dobrze mógłbyś się na nie natknąć napisane w OOP.
Mowa o podejściu gdzie agregaty modeluje się tak aby były relatywnie małe, i bardziej skupiały się na logicznym objęciu samej transakcji/procesie niż obiekcie świata rzeczywistego. Wiem że to może brzmieć zbyt abstrakcyjnie, więc podam prawdziwy przykład z systemu nad którym pracowałem w poprzedniej pracy. Notabene był to i nadal jest projekt gdzie do tej pory widziałem najlepiej zaimplementowane DDD, zarówno w kodzie jak i w zespołowej komunikacji, procesach itp. Mieliśmy wszystko "zgodnie ze sztuką", w tym ekspertów domenowych, event storming itp. Jeśli czegoś mi brakuje z tamtej pracy, to właśnie tego.
Mowa o dużym systemie ubezpieczeniowym dla agentów, a więc ludzi sprzedających i obsługujących polisy dla klientów. No i właśnie- polisy. Pierwsze co przychodzi na myśl człowiekowi myśląc o ubezpieczeniu. Jest więc kuszące aby pomyśleć że jednym z podstawowych i najważniejszych agregatów będzie właśnie polisa, ang. policy. Rzecz w tym że szybko odkryliśmy, że agenci rzadziej mówią o polisach w kontekście czegoś co sprzedają/zmieniają, a raczej mówią co dokładnie zrobili. Przykłady:
Agent nie mówił I sold a policy tylko I placed a new business (policy) with customer.
Agent nie mówił I updated a policy tylko I placed a mid-term adjustment on the policy.
Agent nie mówił I renewed a policy tylko *I placed a renewal.
Przypominam, że stosowaliśmy DDD, a więc odpowiednie nazewcnitwo było kluczowe. To nie my (programiści) mieliśmy dyktować jak rzeczy mają być nazywane, od tego byli użytkownicy systemu i eksperci domenowi. Szybko uświadomiliśmy sobie, że polisa jest pochodną procesów biznesowych, widokiem z którego agent korzysta kiedy chce rozpocząć nowy proces. A ponieważ stosowaliśmy również event sourcing i CQRS, to wiedzieliśmy gdzie jest miejsce takiej pochodnej- w modelu widoku (read side) a nie w modelu domenowym (write side). Co za tym idzie, agregaty jakie mieliśmy w systemie to między innymi:
- NewBusiness
- MidTermAdjustment
- Cancellation
- Renewal
Każdy agregat miał inne niezmienniki (zasady biznesowe) które egzekwował, a dzięki temu że były tak podzielone, to granica transakcyjności była jasna. Jeśli danym procesem był zmiana czegoś w polisie (mid-term adjustment), to dla czego w tym samym agregacie miałyby istnieć zasady dotyczące przerwania polisy (cancellation)? Ewidentnie to dwa różne procesy, a więc dwie różne transakcje.
To wszystko nie oznacza że takie podejście jest jedynym słusznym. Owszem, można mieć system gdzie bardziej skupiamy się na przedmiotach rzeczywistych (policy, student), zamiast procesach (new business, student enrolment), ale moim zdaniem kiedy dobrze zrozumienie się to co opisałem wyżej, to łatwiej modelować i utrzymywać system w oparciu o mniejsze agregaty zorientowane na procesy. Poza tym idzie to w parze z ogólną rekomendacją aby projektować w miarę możliwości mniejsze agregaty.
Myślę że- paradoksalnie- łatwiej jest takie rzeczy ogarnąć kiedy stosuje się DDD razem z CQRS. Wtedy jest jasny podział między stroną obsługi procesów biznesowych (write) od strony widoków (read), więc jest mniejsza pokusa by myśleć o agregatach w kategorii zarówno modelu domeny jak i tego co widać w interfejsie użytkownika. Zresztą nie bez powodu kiedy mowa o DDD (a więc i agregatach) to często w parze idą CQRS i event sourcing- te wszystkie techniki świetnie się uzupełniają.
Vernon sporo o tym pisał, dostępne tutaj.
Tutaj też bardzo fajna prezentacja na ten temat- all our aggregates are wrong. Autro w pewnym momencie- używając właśnie zamówień online jako przykładu- mówi pewne kluczowe kwestie które powinny dać do myślenia:
-
We are modeling behavior, not data
-
The shopping cart doesn't exist