DDD - zagregowany root z dużą liczbą dzieci

10

Przedmówię to pytanie, mówiąc, że jestem stosunkowo nowy w DDD, więc mogę popełnić tutaj kilka podstawowych błędów!

Pracuję nad projektem, który obejmuje pojęcia rachunków i transakcji (w sensie finansowym). Konto może zawierać wiele Transakcji.

Wydaje mi się, że zarówno Konto, jak i Transakcja to Podmioty, a Konto to Łączny katalog główny zawierający Transakcje, ponieważ Transakcja nie może istnieć bez Konta.

Jednak kiedy przyszedłem zastosować to w kodzie, od razu natrafiłem na problem. W wielu sytuacjach nie jest szczególnie przydatne, aby mieć przez cały czas listę każdej transakcji na koncie. Interesuje mnie możliwość wykonywania takich czynności, jak obliczanie salda konta i egzekwowanie niezmienników, takich jak limit kredytu, ale chcę również móc z łatwością pracować z podzbiorem transakcji (np. Wyświetlać te, które mieszczą się w zakresie dat).

W tym drugim przypadku, gdybym korzystał z TransactionRepository, mógłbym skutecznie uzyskać dostęp tylko do tych obiektów, których potrzebuję, bez ładowania całej (potencjalnie bardzo dużej) listy. Pozwoliłoby to jednak na pracę z Transakcjami innymi niż Konto, co oznacza, że ​​złamałem koncepcję Konta jako zagregowanego katalogu głównego.

Jak ludzie radzą sobie z tego rodzaju sytuacjami? Czy akceptujesz tylko konsekwencje pamięci i wydajności ładowania potencjalnie dużej liczby dzieci do zagregowanego katalogu głównego?

Krixon
źródło

Odpowiedzi:

9

Zalecałbym ostrożność z zasadą „nie można istnieć bez” . Mówi to o koncepcji kompozycji w projektowaniu UML / OO i może być jednym z zalecanych podejść do projektowania agregatów w oryginalnej niebieskiej książce DDD (nie jestem pewien), ale od tego czasu została w dużej mierze poprawiona. Lepszym pomysłem może być zobaczenie agregatów z perspektywy granicy transakcji .

Chodzi o to, aby nie tworzyć zbyt dużych agregatów, w których wystąpiłyby problemy z wydajnością, takich jak ten, na który zwracasz uwagę, ani nie były zbyt małe, ponieważ niektóre niezmienniki nieuchronnie obejmowałyby wiele agregatów - powodując problemy z blokowaniem agregatów i współbieżnością.

Właściwy rozmiar agregatu idealnie pasuje do konturów tego, co modyfikujesz w danej transakcji biznesowej, nie więcej i nie mniej. W twoim przykładzie, jeśli nie ma wielu niezmienników domeny obejmujących wiele transakcji finansowych, Transactionnajlepszym rozwiązaniem może być samodzielne utworzenie katalogu głównego agregacji.

guillaume31
źródło
Dziękuję, przeczytam o granicach spójności. Myślę, że twoja sugestia, aby uczynić Transakcję własnym zagregowanym korzeniem, może być dobra; jak mówisz, nie mam wielu niezmienników obejmujących wiele transakcji.
krixon
7

tl; dr - w razie potrzeby złam zasady. DDD nie może rozwiązać wszystkich problemów; w rzeczywistości pomysły, które daje, to dobra rada i dobry początek, ale naprawdę zły wybór niektórych problemów biznesowych. Zastanów się, jak to zrobić.


W przypadku ładowania wszystkich elementów podrzędnych (transakcji) rodzicem (kontem) - Wygląda na to, że napotkałeś problem n + 1 (coś w Google), który rozwiązał wiele ORM.

Możesz go rozwiązać, leniwie ładując dzieci (transakcja) - tylko w razie potrzeby.

Ale wydaje się, że już wiesz, że wspominając, że możesz użyć TransactionRepository, aby rozwiązać problem.

Aby „ukryć” te dane, aby tylko Konto mogło z nich korzystać, nie trzeba nawet przechowywać ich tam, gdzie nikt inny nie byłby w stanie deserializować ich, jak publiczny stół relacyjny. Możesz mieć to zapisane w „dokumencie” konta w bazie danych dokumentu. Tak czy inaczej, jeśli ktoś spróbuje wystarczająco mocno, nadal będzie mógł zobaczyć dane. I „pracuj” z tym. A kiedy nie patrzysz, będą!

Możesz więc skonfigurować uprawnienia, ale musisz uruchomić „konto” jako osobny proces.

Naprawdę zdajesz sobie sprawę, że DDD i czyste użycie modelu obiektowego czasami popycha cię w kąt. Szczerze mówiąc, oczywiście nie musisz używać „składu” / agregatu root, aby skorzystać z zasad projektowania DDD. To tylko jedna rzecz, której możesz użyć, gdy masz sytuację, która pasuje do jej ograniczeń.

Ktoś może powiedzieć „nie wcześnie optymalizuj”. Tutaj w tym przypadku znasz odpowiedź - będzie wystarczająca liczba transakcji, aby znaleźć metodę, która zachowa je wszystkie na zawsze na koncie.

Prawdziwą odpowiedzią jest rozpoczęcie SOA na stojąco. W moim miejscu pracy obejrzeliśmy filmy Udi Dahan „Przetwarzanie rozproszone” i kupiliśmy nServiceBus (tylko nasz wybór). Zrób usługę dla kont - z własnym procesem, kolejkami komunikatów, dostępem do bazy danych relacji, którą tylko ona widzi, i ... viola, możesz na stałe zakodować instrukcje SQL w programie, a nawet wrzucić kilka skryptów transakcyjnych Cobola (żartowanie) oczywiście), ale poważnie rozdzielaj obawy, niż najmądrzejszy snob OO / Java kiedykolwiek mógł marzyć.

W każdym razie poleciłbym to dobrze modelować; możesz po prostu czerpać korzyści z agregacji katalogu głównego bez problemów, traktując usługę jako mini-ograniczony tekst.

Ma to oczywiście wadę. Nie możesz po prostu RPC (usługa sieciowa, SOAP lub REST) ​​wchodzić i wychodzić z usług i między nimi, lub dostajesz anty-wzorzec SOA zwany „węzłem” z powodu czasowego połączenia. Musisz użyć odwrócenia wzorca komunikacji, znanego również jako „Pub-Sub”, który podobnie jak procedury obsługi zdarzeń i generatory zdarzeń, ale (1) między procesami (które można umieścić na osobnych komputerach, jeśli zostaną przeciążone na jednym).

prawdziwym problemem jest to, że nie chcesz, aby usługa, która musi pobierać dane z innej usługi, „blokowała” lub czekała - musisz odpalić i zapomnieć komunikat i pozwolić programowi obsługiwać gdzie indziej w twoim programie, aby zakończył przetwarzanie. Oznacza to, że musisz robić swoją logikę inaczej. nServicebus automatyzuje wzorzec „saga”, aby w tym pomóc, ale ostatecznie musisz opracować inny styl kodowania. Nadal możesz to wszystko zrobić, po prostu musisz zrobić to inaczej!

Książka „Wzory SOA” Arnona Rotema-Gal-Oza odpowiada na wiele pytań na ten temat. Łącznie z użyciem „wzorca aktywnej usługi” do okresowej replikacji danych z usług zewnętrznych do własnych, gdy zajdzie taka potrzeba (potrzebnych byłoby wiele RPC lub link jest zawodny / nie występuje w ekosystemie publikowania / subskrypcji).

Tylko do podglądu, UI nie muszą RPC do usług. Raporty są generowane z bazy danych raportowania zasilanej przez bazy danych usług. Niektórzy twierdzą, że raporty nie są potrzebne i że problem należy rozwiązać w inny sposób. Bądź sceptyczny wobec tej rozmowy.

W końcu jednak nie wszystkie rzeczy można właściwie zaklasyfikować do jednej usługi. Świat nie działa na ravioli! Będziesz musiał łamać zasady. Nawet jeśli nigdy nie będziesz musiał, nowi twórcy projektu zrobią to, gdy go opuścisz. Ale nie martw się, jeśli zrobisz, co możesz, 85% przestrzeganie zasad sprawi, że program będzie o wiele łatwiejszy w utrzymaniu.

Wow, to było długie.

FastAl
źródło
Dzięki za szczegółową odpowiedź, na pewno trochę poczytam o SOA.
krixon