98. O agregatach, eventach i Dynamic Consistency Boundary z Pawłem Pacaną
W świecie Domain-Driven Design, Agregat jest powszechnie uznawany za jeden z fundamentalnych wzorców odpowiedzialnych za spójność danych. To on wyznacza granicę transakcyjną, wewnątrz której pilnujemy niezmienników biznesowych, gwarantując integralność naszego modelu. Ale co w sytuacji, gdy ta z góry zdefiniowana, statyczna granica staje się pewnym ograniczeniem? Czy w każdym procesie biznesowym potrzebujemy dokładnie tego samego, silnego poziomu spójności i czy sztywny podział na agregaty zawsze idealnie odzwierciedla dynamiczną naturę problemu, który modelujemy?Okazuje się, że możemy podejść do tego zagadnienia w bardziej elastyczny sposób. W tym odcinku, wraz z moim gościem, Pawłem Pacaną z firmy Arkency, dokładnie przyjrzymy się koncepcji Dynamic Consistency Boundary. Porozmawiamy o tym, jak można myśleć o spójności nie jako o statycznej, raz ustalonej granicy, ale jako o koncepcji, która dopasowuje się do kontekstu konkretnej operacji biznesowej.W tym odcinku usłyszysz między innymi o:trudnościach w projektowaniu i długoterminowym utrzymaniu agregatów w systemieDynamic Consistency Boundary i czym ten wzorzec różni się od klasycznego podejścia z agregatemtagowaniu i linkowaniu zdarzeń pomiędzy strumieniamiwymaganiach dla event-store, aby stosowanie Dynamic Consistency Boundary było w ogóle możliwe pułapkach, na które należy zwrócić szczególną uwagę, by wykorzystanie DCB nie stało się problemMateriały dodatkowe do tego odcinka znajdują się na stronie tego odcinka na bettersoftwaredesign.pl.YouTube Alert! Odcinki podcastu są także dostępne na moim kanale na YouTube. Warto zasubskrybować, aby być na bieżąco z kolejnymi odcinkami.
Jestem jednym z liderów projektu Realize Event Store, więc implementując Realize Event Store też mnie takie tematy interesują, czego ludzie używają albo jakie koncepty tam się pojawiają. I przyglądałem się trochę temu tematowi. Też w ramach takich spotkań firmowych, takiego book clubu, bierzemy sobie różne tematy i je rozkładamy na czynniki. Czyli to DCB pojawiło się właśnie jako jeden z tematów, które sobie analizowaliśmy, przegadaliśmy, skonfrontowaliśmy. Geneza tego tematu pochodzi od Sary Perlgini, jeżeli dobrze teraz wszystko pamiętam. Zgadza się. Ona miała taką prezentację Kill the Aggregates, w którym troszeczkę kwestionowała koncept agregatów i proponowała zupełnie inne podejście, które powiedziałbym będzie działać w świecie właśnie event store'ów, w świecie opartym o eventy i chyba nie znajdzie zastosowania tam, gdzie tych eventów nie ma, bo cała ta geneza opiera się właśnie o przeszukiwanie odpowiedniego streamu zdarzeń.
Być może nie wszystkie operacje na tym agregacie potrzebują całej części stanu, w związku z tym do podjęcia decyzji potrzebujemy jakiś mały fragment, a tę granicę rozszerzamy, bo ona jest zdefiniowana przez wszystkie operacje w agregacie. Przy DCB moglibyśmy, przynajmniej takie są jego założenia, tę granicę płynnie zmieniać, zmniejszać. Dzięki temu będziemy mieli mniejszy contention do podjęcia. Opując to jakby na relacyjną bazę danych, krótko mówiąc, selektem wyciągnę inne dane. Trochę tak. Zawężamy ten zakres, w którym operujemy. Jeżeli będziemy mieć jakieś konflikty, to tylko na tym mniejszym zakresie, na tej mniejszej granicy spójności, bo operujemy tylko na danych, które są niezbędnie potrzebne do podjęcia danej decyzji.
Zgadza się. Event Store musiałby być zmodyfikowany właśnie w taki sposób, żeby wspierać DCB. Czy tutaj nie pachnie to jakimś problemem wydajnościowym? Bo de facto chyba tę kwerendę muszę wykonać dwa razy? Tak jest. Musimy się upewnić, że możemy zapisać. Bo ten temat taki infrastrukturalny tutaj chyba nie jest czymś, obok czego mogę przejść tak obojętnie. Chociażby to, o czym tutaj wspomnieliśmy. Drugi temat, który tutaj przynajmniej mi się pojawia tak na radarze, to jest to, że ja muszę te zdarzenia jakoś oznaczać, tagować, żeby wiedzieć, które zdarzenia chciałbym dokładnie wyszukiwać. Nie wiem, czy mogę to zrobić po... payloadzie tych zdarzeń, które wcześniej zapisałem, czy muszę dodatkowo jakieś je oznaczać jakimiś tagami, tak żeby mieć pewność, że baza danych zoptymalizuje samą kwerendę?
Na potrzeby wykonania mojego zapytania. Z punktu widzenia event store'a chcielibyśmy raczej, żeby ten payload danych, które mamy w zdarzeniach, był dla nas taki nieprzejrzysty. Te dane mogą być w jakimś binarnym formacie, nieprzyszukiwalnym, mogą być skompresowane, mogą być wręcz zaszyfrowane, więc wydaje się, że nie chcielibyśmy ich zawsze szukać w payloadzie. Może są szczególne przypadki, gdzie może być nam to łatwo w naszej implementacji. Natomiast ogólnie nie szukałbym ich w payloadzie. W samym DCB pojawia się koncepcja właśnie takiego tagowania, czyli zdarzenie może przynależeć, może mieć wiele tagów i tymi tagami opisujemy, że jest istotne w przykładzie z kursami i studentami dla jakiegoś konkretnego studenta albo dla jakiegoś konkretnego kursu.
Mniej zdarzeń potrzebujemy, bo zmieniła się reguła, której musimy pilnować. Wciąż te zdarzenia są w ramach jakiegoś taga, należą do jakiegoś konkretnego strumienia ad hoc, bo ten identyfikator trochę je tam kieruje. Tak, zapomniałem, że rozmawiałem z fachowcem od refaktoryzacji, z racji tego, że agencji to po prostu refaktoryzacja w DNA. Wydaje mi się, że refaktoryzacja... Chociaż powinniśmy ją wszyscy mieć, wiesz, jako branżę. Właśnie, właśnie. Z punktu widzenia refaktoryzacji wydaje mi się, że to jest w porządku. Natomiast do mnie ten argument do refaktoryzacji, która jest łatwiejsza, z DCB nie trafiał, bo my już w Age Event Store mieliśmy taki design, że można było linkować zdarzenia do różnych strumieni i tak naprawdę zmiana granic tego agregatu, jak on się mógł zmieniać w czasie, to było trochę przelinkowanie zdarzeń do innego strumienia i później zmiana kodziku, który sobie te zdarzenia czytał i operował na tym nowym strumieniu.
z którejś wersji karteczek do takiego kodu. A przy okazji rozwiązujesz w ten sposób two hard things problem, tak? Naming things and cache invalidation. Tak. Przynajmniej jeden odpada. Tak, tak, tak. No ale tak, nazewnictwo rzeczy czasami bywa dosyć problematyczne. Problemy z DECB. To jest bardzo dobre pytanie, bo wspomniałeś o jednym, czyli to, że być może będziemy mieli problem przy samym zapisie do EventStore'a, bo musimy zrobić to zapytanie, którego w podejściu z nazwanym strumieniem i pilnowaniem jakiegoś constrainta, jeżeli to byłaby implementacja oparta relacyjna bazy danych, byśmy nie mieli. Po prostu przy zapisie ten constraint zostanie złamany lub nie. Jeżeli zostanie złamany, to zapis się nie udaje. Tutaj będziemy musieli faktycznie zrobić query albo jakąś inną sprytną operację, żeby się zorientować, że nikt w międzyczasie nam tej kolekcji nie zmodyfikował.
Za duży strumień do odczytu i przetworzenia. Jasne. Bo to jest trochę tak, że w ramach samego agregatu chcemy być ściśle spójni. Jeżeli będziemy mieli kilka takich mniejszych agregatów, które ze sobą współpracują, no to już ta współpraca między agregatami jest ostatecznie spójna. Każdy agregat ładujemy z w miarę krótkich strumień. Natomiast płacimy za to jakąś cenę w postaci złożoności i jakby synchronizowania później tych mniejszych agregatów ze sobą. Pojawia się tam na pewno jakiś proces do skoordynowania tego i ewentualnie do odwrócenia zmian, które były niepożądane w całości. Wydaje się, że przy tym DCB mamy taką pokusę, że teraz mamy młotek, w którym możemy rozwiązywać problemy w sposób ściśle spójny, bo teraz zrobimy sobie taką dynamiczną granicę, żeby na pewno... Taką, jaką potrzebujemy w tym momencie, żeby na pewno to rozwiązać w taki sposób, żeby właśnie...
Wydaje mi się, że samo to zaciąganie zdarzeń z dosłownie każdego zakątka systemu jest trochę bardziej ograniczony, bo wciąż operujemy w ramach jakichś tam tagów, czy tych identyfikatorów domenowych, jak je nazywa Sara. A one są już ściśle w ramach jakiegoś baudet kontekstu, więc wydaje mi się, że jednak poruszamy się w jakimś ograniczonym obszarze. Natomiast zgodzę się co do tego, że trochę spekulujemy, jak dobre będzie to DCB, albo jakie problemy natrafimy, a dowiemy się dopiero za jakiś czas. Skala na pewno jest szeroka. Ale tak, to byłoby rozwiązanie. Ograniczmy się tylko i wyłącznie do zdarzeń z danego kontekstu i wtedy powinno być tacy. Jeżeli tutaj nasze konteksty zostały dobrze wyznaczone, brzmi jak całkiem sensowne rozwiązanie.
Jak wygląda w praktyce używanie tego w jakichś istniejących narzędziach? To są trzy rzeczy, które takie narzędzie musiałoby spełniać. Raz, że musi pozwalać na takie kwerendy, gdzie wybieramy zdarzenie o typie i powiązane z jakimiś identyfikatorami i raczej nie chcemy zaglądać w sam payload eventów, czyli musimy mieć już to tagowanie. To właśnie ten drugi element to jest tagowanie tych zdarzeń, żeby one mogły należeć do jakiegoś strumienia. Tak to nazwałem strumieniem. W DCB nie nazwałem tego strumieniami, tylko to są tagi, ale powiedzmy, że tak, pod tytułem student1. Trochę można sobie zrobić taką mapę w głowie z nazwą strumienia. A trzecia rzecz to jest właśnie ten append, który zmienia się to, że my nie dopisujemy zdarzeń do jakiegoś nazwanego strumienia, tylko właśnie dopisujemy je do globalnego strumienia, ale mechanizm optimisty, konkurencji, kontrol działa już na jakiejś kolekcji na tej kwerendzie.
Conditional append. Kiedy wsparcie tego? W event store? Myślę, że wkrótce, najchętniej w najbliższym wydaniu. Dwie trzecie, czyli dwa z tych trzech warunków, po prostu już spełniamy. Nie dlatego, że zrobiliśmy je dla DCB, po prostu tak kiedyś zaprojektowaliśmy event store i okazało się, że mamy bardzo dużo wspólnego z DCB. Natomiast ta najtrudniejsza część i ta, gdzie mamy potencjalnie do rozwiązania problemy wydajnościowe i zbadania, czy one rzeczywiście wystąpią, to jest ten conditional append. To jest ten brakujący klocek. Natomiast już dzisiaj, jeżeli chcemy poeksplorować same przykłady DCB na takim systemie, który nie jest produkcyjny i problemy ze współbieżnością tam nie będą występować, no to sam problem, który Sara Pelegirni używa do przedstawienia konceptu dynamic consistency boundary, czyli studenci i kursy,
jest zaimplementowany w TypeScriptie na stronie dcb-events. My ten przykład i ten kodik z TypeScripta przenieśliśmy na Rubiego i na Edge Event Store i można sobie zobaczyć, jak te przykłady w Edge Event Store wyglądają, ze wsparciem w dwóch trzecich i z tą nakładką na append, która na potrzeby przykładów działa. W produkcyjnym systemie musimy to zaimplementować po prostu. W swoim arsenale masz tak naprawdę dwa narzędzia. Masz wzorzec Aggregate, który oferuje ci tą statyczną granicę. Jest dosyć jednak rozpowszechnionym wzorcem i ma tam troszeczkę powiedzmy plusów i minusów. Obok jest drugie narzędzie Dynamic Consistency Boundary. W jakich przypadkach zdecydowałbyś się na jedno i drugie rozwiązanie? Kiedy byś użył agregatu? A kiedy byś użył takiego klasycznego, nawet prezystowanego do bazy danych, gdzie ta struktura tego agregatu jest już ściśle zdefiniowana?
A kiedy byś się zdecydował na właśnie taki dynamiczny odczyt z różnych strumieni i pod jakimi warunkami? Wydaje mi się, że kuszące byłoby poeksplorowanie DCB tam, gdzie musielibyśmy koordynować ze sobą dwa agregaty. Kusząca jest ta wizja, że być może unikniemy tego procesu w ramach tego samego bounded kontekstu między koordynacją dwóch agregatów. Bo o ile te procesy są bardzo spoko do koordynowania różnych bounded kontekstów, które są ze sobą z natury luźno powiązane, o tyle w ramach samego bounded kontekstu, to już może być trochę zagadkowe, bo patrzę w ten kodzik i wydaje mi się, że to już jest całość, ale to jeszcze nie jest całość, bo gdzieś tam pod spodem jest proces, który to spaja, a ja muszę trochę wiedzieć o tym, że jest ten proces. Albo mam dobrą narzędziówkę, która mi to mówi i pokazuje, jak to wygląda z lotu ptaka, albo trochę muszę wiedzieć, co chcę znaleźć.
Poruszyłeś ciekawą rzecz z deciderem, bo to jest tak, że pozbywamy się tego agregata, ale problem nie znika. Ciągle musimy mieć coś, czym wypełnimy. To mięsko w środku gdzieś musi być jakoś zagospodarowane. To jest też ciekawe w DCB, że trochę może nam się zmienia optyka, bo patrzymy już na taki wycinek, na jedną klatkę. Ta klatka nazywa się obsługa jakiejś jednej operacji, jakiejś jednej komendy. I co my musimy spełnić w ramach tej jednej komendy, żeby podjąć decyzję? Ta decyzja jest oparta na zbiór zdarzeń, więc wybieramy sobie, ładujemy te zdarzenia, które nam mają pomóc podjąć tę decyzję, spełnić reguły. No i właśnie to jest to w środku, niezdefiniowane. Czym jest to w środku? Wydaje się, że decider tutaj pasuje fajnie.
Czy jakiś styl lepiej uwypukla rozwiązania, czy gorzej. Takie porównanie side by side dwóch rzeczy można sobie z tego repo zrobić. Implementacji jest tam już chyba kilkanaście. I podobną koncepcję mamy do repozytorium dynamic consistency boundary, gdzie mamy jeden przykład i tą implementację na razie typescriptową przeniesioną 1 do 1 do Ruby'ego, ale pojawi się tam też implementacja bardziej idiomatyczna Ruby'owa. Jeżeli ktoś chce się przekonwertować na Ruby'ego, to myślę, że to będzie raczej ta implementacja. Ale też chcemy tam pokazać dla kontrastu różne implementacje oparte o agregaty. Trochę też takie na brzegach spektrum, żeby może bardziej uwypuklić jakieś problemy i zastanowić się, czy coś na tych brzegach spektrum DCB robi lepiej.
I też coś w środku, coś bardzo bliskiego od DCB. Ja bym jeszcze odesłał do tej strony, o której wspomniałeś. DCB Events. Tak, to jest strona, która ma trochę taką, nie wiem, czy formalną, czy nie, ale specyfikację tego, co Event Store musiałby wspierać. Trochę klaruje nam, czym jest DCB i ma bardzo bogatą listę przykładów, jak takie typowe problemy właśnie z zapewnianiem unikalności, czyli słynny przykład z kursami i studentami, jak jest implementować DCB. Przykłady są na tej stronie w TypeScriptzie. Jeszcze odesłał do dokumentacji Axona, który też stara się zaimplementować to podejście. W zasadzie możemy chyba powiedzieć, że Axon ma już wsparcie dla DCB. Wsparcie jest oznaczone jako beta. Czyli na twoją odpowiedzialność używasz tą własną odpowiedzialność.
Pokazano wszystkie 15 dopasowań. Transkrypcja generowana automatycznie i niesprawdzana ręcznie — może zawierać błędy.
Kliknij, aby znaleźć fragmenty, w których pada.
W świecie Domain-Driven Design, Agregat jest powszechnie uznawany za jeden z fundamentalnych wzorców odpowiedzialnych za spójność danych. To on wyznacza granicę transakcyjną, wewnątrz której pilnujemy niezmienników biznesowych, gwarantując integralność naszego modelu. Ale co w sytuacji, gdy ta z góry zdefiniowana, statyczna granica staje się pewnym ograniczeniem?
Czy w każdym procesie biznesowym potrzebujemy dokładnie tego samego, silnego poziomu spójności i czy sztywny podział na agregaty zawsze idealnie odzwierciedla dynamiczną naturę problemu, który modelujemy?
Okazuje się, że możemy podejść do tego zagadnienia w bardziej elastyczny sposób. W tym odcinku, wraz z moim gościem, Pawłem Pacaną z firmy Arkency, dokładnie przyjrzymy się koncepcji Dynamic Consistency Boundary. Porozmawiamy o tym, jak można myśleć o spójności nie jako o statycznej, raz ustalonej granicy, ale jako o koncepcji, która dopasowuje się do kontekstu konkretnej operacji biznesowej.
W tym odcinku usłyszysz między innymi o:
trudnościach w projektowaniu i długoterminowym utrzymaniu agregatów w systemieDynamic Consistency Boundary i czym ten wzorzec różni się od klasycznego podejścia z agregatemtagowaniu i linkowaniu zdarzeń pomiędzy strumieniamiwymaganiach dla event-store, aby stosowanie Dynamic Consistency Boundary było w ogóle możliwe pułapkach, na które należy zwrócić szczególną uwagę, by wykorzystanie DCB nie stało się problemMateriały dodatkowe do tego odcinka znajdują się na stronie tego odcinka na bettersoftwaredesign.pl.
YouTube Alert! Odcinki podcastu są także dostępne na moim kanale na YouTube. Warto zasubskrybować, aby być na bieżąco z kolejnymi odcinkami.