DDD - optymalizacja operacji na dużych kolekcjach wchodzących w skład agregatu i paginacja

0
TomRZ napisał(a):

Rootem agregatu może być równie dobrze para: identyfikator wątku + numer strony które razem stworzą unikalny klucz podstawowy. W ten sposób unikasz metody getPosts(int page)

Jeśli dobrze zrozumiałem to każda strona będzie miała osobną tożsamość, gdzie w tym wypadku będą istotne informacje o wątku jako całości? Nie wydaje mi się to dobre rozwiązanie ale może źle zrozumiałem

Możesz stworzyć dwa agregaty - jeden do analizy całościowej, a drugi do stronnicowania.

Obydwa będą miały różny root id / pk - bo ten do stronnicowania będzie złożony z id wątku + numer strony a całościowy tyko id wątku.

8

Na wstępie zaznaczam- nie ma złotych środków i jedynie proponuję jedno z możliwych rozwiązań.

Jak zawsze, należy zachować zdrowy rozsądek i wybrać optymalne rozwiązanie. Zwróć uwagę że napisałeś:

Wydaje się logiczne, że post nie ma globalnej tożsamości więc będzie wchodził w skład wątku.

Agregat to przede wszystkim granica transakcyjności (transactional boundary, nie wiem czy tak to się tłumaczy na język polski). Należy się kierować przede wszystkim tą zasadą kiedy myśli się o modelowaniu agregatów. Również nie ma nic złego w bardzo małych, jedno-encyjnych agregatach jeśli ma to sens i spełnia warunek pierwszej zasady. Piszę to dla tego że należało by odpowiedzieć sobie na pewne pytania:

  • Czy zmiana treści postu wymaga zaangażowania wątku?
  • Czy zmiana treści wątku (np. tytuł) wymaga zaangażowania postów?
  • Czy posty mogą zostać usunięte oddzielnie od usuwanego właśnie wątku?

Dodatkowo dochodzi wymieniony przez Ciebie problem potencjalnie dużej ilości postów w wątku, oraz samych postów mających dodatkowe atrybuty (nie mylić z atrybutami w kodzie) takie jak polubienia. Wracamy więc do pytań:

  • Czy załadowanie wątku oznacza konieczność załadowania wszystkich postów wraz z ich "wewnętrznymi" atrybutami?
  • Czy polubienie postu jest elementem "transakcji" całego wątku?

Moim zdaniem na podstawie tych pytań i informacji które podałeś wynika że Post jest świetnym kandydatem aby być agregatem samym w sobie, z encją Post jako root oraz np. Polubienie jako value object zawierający ID Posta i użytkownika który to polubił.

@Charles_Ray wspomniał że Wątek może być tylko view modelem Poszedł w dobrym kierunku, problem tylko że- jak mniemam- wątek również może być edytowany (tytuł, treść?) oraz zamykany i/lub usuwany. W tej sytuacji Wątek to również kandydat na agregat. Natomiast wspomnienie modelu widoku jest jak najbardziej trafne. Należy pamiętać że agregaty należą do warstwy modelu domeny, i same w sobie nie powinny mieć nic wspólnego z obsługą widoku. Co za tym idzie zainteresuj się CQRS i Materialized View. Wydaje mi się że Twój scenariusz wymaga takiego zastosowania- na podstawie tego co zachodzi w domenie będziesz mógł budować widok obsługujący konkretne wymagania- w Twoim przypadku zwracający wątek (a konkretnie informacje o wątku) oraz podzbiór postów filtrowany na podstawie strony którą aktualnie przegląda użytkownik.

1

Matko przenajświętsza, przecież Post i Wątek to dwa agregaty.

A jak będziesz chciał moderować treść postu, to potrzebujesz do tego Wątek?

0

@Aventus
Bardzo dziękuje za merytoryczną odpowiedź, od niedawna zajmuję się DDD i kilka napisanych przez Ciebie linijek
zmieniło moje postrzeganie tego tematu.

Co do CQRS to celowo o nim nie wspomniałem dlatego, że to rozwiązanie wydaje mi się zbyt drastyczne w przypadku
wydaje się prostego problemu jak paginacja. Wyobraźmy sobie, że mamy już stworzony dosyć rozwinięty model
domeny i nagle ze względów technicznych zachodzi potrzeba stronicowania jakiejś kolekcji, która wchodzi w skład agregatu.
Wprowadzenie CQRS z wyżej wymienionego powodu wydaje mi się zbyt drastyczne. Spotkałem się z jednak
z zapożyczoną z CQRS ideą Finder czyli klasy znajdującej się w warstwie aplikacji, która posiada wiedzę o wewnętrznej
strukturze agregatu i pozwala na odpytywanie o jego wewnętrzne elementy, nie jestem jednak pewien czy
nie przeczy to idei agregatu.

@Charles_Ray
Tak jak napisałem wyżej wydaje mi się, że w ten sposób zbliżamy się w stronę CQRS

@yarel
Dziękuję za przygotowany diagram, co do paginacji w warstwie domeny to może podałem zły przykład,
ponieważ możemy się tutaj na siłę doszukiwać znaczenia paginacji w dziedzinie jednak chodziło
mi o sytuację w której paginacja jest elementem wyłącznie technicznym, zwiększającym wydajność, elementem ui.

@WyznawcaDDD
Tak jak wspomniałem wyżej, nie chodzi mi konkretnie o problem forum ale o sytuacje w której musimy stronicować kolekcję która wchodzi w skład agregatu.
Szczerze mówiąc DDD zajmuję się od niedawna więc sytuacja jest czysto teoretyczna, nie wiem czy kiedykolwiek zajdzie taka potrzeba.

2

Co do CQRS to celowo o nim nie wspomniałem dlatego, że to rozwiązanie wydaje mi się zbyt drastyczne w przypadku
wydaje się prostego problemu jak paginacja. Wyobraźmy sobie, że mamy już stworzony dosyć rozwinięty model
domeny i nagle ze względów technicznych zachodzi potrzeba stronicowania jakiejś kolekcji, która wchodzi w skład agregatu.
Wprowadzenie CQRS z wyżej wymienionego powodu wydaje mi się zbyt drastyczne.

Myślę że tutaj mylne są dwie kwestie. Po pierwsze w warstwie domeny chcesz osiągnąć coś co do domeny nie należy- paginacja na potrzeby widoku. Raz jeszcze zaznaczam że agregat to przede wszystkim granica transakcyjności. Paginowanie postów dla widoku nie leży w gestii tej transakcyjności.

Druga sprawa to że jak wiele osób zapewne mylisz podstawy CQRS z tym jak używa się go na poziomie infrastruktury- mając oddzielne bazy danych a czasem nawet serwisy. CQRS to natomiast coś znacznie prostszego, i fundamentalnie oznacza podział pomiędzy klasami odpowiadającymi za modyfikacje stanu aplikacji (polecenia- commands) oraz za odczyt tego stanu (zapytania- queries). Jeśli jest to więc odpowiednie rozwiązanie w Twoim przypadku, nic nie stoi na przeszkodzie aby korzystać z tej samej bazy danych. Różnica w tym że zamiast używać agregatu do załadowania widoku, skorzystasz z oddzielnej klasy reprezentującej widok która np. zostanie zbudowana na podstawie zapytania SQL stworzonego specjalnie na potrzeby tego widoku.

3

Jak @Aventus dobrze prawi: model układasz sobie w oparciu o reguły biznesowe (które musza być spójne natychmiast), które sprawdzasz przy zapisie, natomiast paginacja, grupowanie danych to kwestia odczytu danych, które mogą operować na innym modelu (zoptymalizowanym pod odczyty właśnie). Najważniejsza lekcja moim zdaniem - nie myśl o modelu w kategoriach widoków.

0

@Aventus @Charles_Ray
Rozumiem i jak najbardziej ma to sens, ale jeszcze dopytam, czy warstwy zapisu i odczytu CQRS w kontekście DDD
to odpowiednio warstwa domeny i dodatkowa warstwa odczytu? Czy flow w tym przypadku mógłby wyglądać następująco:

  • UI - Przekazuje Command lub Query do CommandService lub QueryService warstwy aplikacji
  • Application - CommandService lub QueryService zarząda elementami Command Model (Domain) lub Query Model
  • Query Model - Zawiera modele widoku (np. projekcje agregatów) oraz interfejsy repozytoriów potrzebne do ich odczytu
  • Command Model (Domain) - to co w DDD
  • Infrastructure - to co w DDD
1

Jak dla mnie to wygląda OK, poza ostatnim- nie za bardzo rozumiem co infrastruktura ma do DDD. Infrastruktura to infrastruktura dostarczająca implementacji dla abstrakcji wyżej.

1

@Aventus
Tak i o to właśnie mi chodziło. Jeszcze raz dziękuje za pomoc, bardzo mi to pomogło

1

To ja tu tylko zostawię jako taki mały suplement: https://pl.wikipedia.org/wiki/Kulty_cargo

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0