Od jakiegoś czasu flirtuję z Event Sourcing i CQRS, chociaż nigdy nie miałem okazji zastosować wzorców w prawdziwym projekcie.
Rozumiem korzyści płynące z rozdzielenia problemów związanych z czytaniem i pisaniem i doceniam to, w jaki sposób Event Sourcing ułatwia projektowanie zmian stanu w bazach danych „Read Model”, które różnią się od Twojego Event Store.
Nie jestem do końca pewien, dlaczego kiedykolwiek nawadniałeś swoje Agregaty z samego Sklepu Eventowego.
Jeśli prognozowanie zmian w bazach danych „do odczytu” jest tak łatwe, dlaczego nie zawsze projektować zmiany w bazie danych „do zapisu”, których schemat idealnie pasuje do modelu domeny? Byłaby to skutecznie baza danych migawek.
Wyobrażam sobie, że musi to być dość powszechne w aplikacjach ES + CQRS na wolności.
W takim przypadku, czy Event Store jest przydatny tylko podczas przebudowy bazy danych „write” w wyniku zmian schematu? A może brakuje mi czegoś większego?
źródło
Odpowiedzi:
Ponieważ „wydarzenia” to księgi rekordów.
Tak; czasem użyteczną optymalizacją wydajności jest użycie buforowanej kopii stanu agregacji, a nie generowanie tego stanu od nowa za każdym razem. Pamiętaj: pierwszą zasadą optymalizacji wydajności jest „nie”. Zwiększa to złożoność rozwiązania i wolisz go unikać, dopóki nie uzyskasz przekonującej motywacji biznesowej.
Brakuje czegoś większego.
Po pierwsze, jeśli zastanawiasz się nad rozwiązaniem pochodzącym od zdarzenia, dzieje się tak dlatego, że oczekujesz wartości w zachowaniu historii tego, co się wydarzyło, to znaczy, że chcesz wprowadzać nieniszczące zmiany .
Dlatego w ogóle piszemy do sklepu z wydarzeniami.
W szczególności oznacza to, że każdą zmianę należy zapisać w magazynie zdarzeń.
Rywalizujący pisarze mogą potencjalnie albo zniszczyć nawzajem swoje zapisy, albo doprowadzić system do niezamierzonego stanu, jeśli nie znają się nawzajem. Tak więc zwykłym podejściem, gdy potrzebujesz spójności, jest skierowanie swoich zapisów do określonej pozycji w czasopiśmie (analogicznie do warunkowego PUT w interfejsie HTTP). Niepowodzenie zapisu informuje pisarza, że ich bieżące rozumienie dziennika nie jest zsynchronizowane i że powinni się zregenerować.
Powrót do znanej dobrej pozycji, a następnie odtworzenie wszelkich dodatkowych zdarzeń, ponieważ ten punkt jest powszechną strategią odzyskiwania. Ta znana dobra pozycja może być kopią tego, co znajduje się w lokalnej pamięci podręcznej lub reprezentacją w sklepie z migawkami.
Na szczęśliwej ścieżce możesz zachować migawkę agregatu w pamięci; wystarczy skontaktować się z zewnętrznym sklepem, gdy nie ma dostępnej kopii lokalnej.
Co więcej, nie musisz być całkowicie pochłonięty, jeśli masz dostęp do księgi rekordów.
Tak więc zwykłym podejściem ( jeśli używa się repozytorium migawek) jest utrzymywanie go asynchronicznie . W ten sposób, jeśli musisz odzyskać, możesz to zrobić bez ponownego ładowania i odtwarzania całej historii agregatu.
W wielu przypadkach ta złożoność nie jest interesująca, ponieważ drobnoziarniste agregaty o określonym czasie życia zwykle nie gromadzą wystarczającej liczby zdarzeń, aby korzyści przewyższyły koszty utrzymania pamięci podręcznej migawki.
Ale gdy jest to właściwe narzędzie do rozwiązania problemu, wczytanie nieaktualnej reprezentacji agregatu do modelu zapisu, a następnie zaktualizowanie go o dodatkowe zdarzenia, jest całkowicie rozsądnym rozwiązaniem.
źródło
Ponieważ nie określasz, jaki byłby cel bazy danych „zapisu”, przyjmuję tutaj, że masz na myśli to: rejestrując nową aktualizację w agregacji, zamiast odbudowywać agregację ze sklepu zdarzeń, usuń go z bazy danych „write”, sprawdź poprawność zmiany i wydaj zdarzenie.
Jeśli to masz na myśli, ta strategia stworzy warunek niespójności: jeśli nowa aktualizacja nastąpi, zanim ostatnia będzie miała szansę na przejście do bazy danych „zapisu”, nowa aktualizacja zostanie zweryfikowana pod kątem nieaktualnych danych, potencjalnie powodując w ten sposób zdarzenie „niemożliwe” (tj. „niedozwolone”) i niszcząc stan systemu.
Weźmy na przykład stały przykład rezerwacji miejsc w teatrze. Aby zapobiec podwójnej rezerwacji, musisz upewnić się, że zarezerwowane miejsce nie jest już zajęte - to właśnie nazywasz „sprawdzaniem poprawności”. Aby to zrobić, przechowujesz listę już zarezerwowanych miejsc w bazie danych „write”. Następnie, gdy pojawi się prośba o rezerwację, sprawdzasz, czy żądane miejsce znajduje się na liście, a jeśli nie, wydaje wydarzenie „zarezerwowane”, w przeciwnym razie odpowiada komunikatem o błędzie. Następnie uruchamiasz proces projekcji, w którym słuchasz „zarezerwowanych” zdarzeń i dodajesz zarezerwowane miejsca do listy w bazie danych „zapisu”.
Normalnie system działałby w następujący sposób:
Co jednak się stanie, jeśli żądania pojawią się zbyt szybko, a krok 5 nastąpi przed krokiem 4?
Teraz masz dwa wydarzenia na rezerwację tego samego miejsca. Stan systemu jest uszkodzony.
Aby temu zapobiec, nigdy nie należy sprawdzać poprawności aktualizacji względem projekcji. Aby sprawdzić poprawność aktualizacji, przebuduj agregację ze sklepu zdarzeń, a następnie sprawdź poprawność aktualizacji względem niej. Następnie wydajesz zdarzenie, ale używasz osłony znacznika czasu, aby upewnić się, że nie wydano żadnych nowych zdarzeń od czasu ostatniego czytania w sklepie. Jeśli to się nie powiedzie, po prostu spróbuj ponownie.
Odbudowywanie agregatów ze sklepu zdarzeń może wiązać się z obniżeniem wydajności. Aby temu zaradzić, możesz przechowywać zbiorcze migawki bezpośrednio w strumieniu zdarzeń, oznaczone tagiem ID zdarzenia, z którego utworzono migawkę. W ten sposób można odbudować agregację, ładując najnowszą migawkę i odtwarzając tylko zdarzenia, które nastąpiły po niej, zamiast zawsze odtwarzać cały strumień zdarzeń od początku.
źródło
Głównym powodem jest wydajność. Możesz przechowywać migawkę dla każdego zatwierdzenia (zatwierdzenie = zdarzenia generowane przez pojedyncze polecenie, zwykle tylko jedno zdarzenie), ale jest to kosztowne. Wzdłuż migawki musisz przechowywać także zatwierdzenie, w przeciwnym razie nie byłoby to pozyskiwanie zdarzeń. A wszystko to musi odbywać się atomowo, wszystko albo nic. Twoje pytanie jest ważne tylko wtedy, gdy używane są osobne bazy danych / tabele / kolekcje (w przeciwnym razie byłoby to dokładnie pozyskiwanie zdarzeń), więc jesteś zmuszony używać transakcji w celu zagwarantowania spójności. Transakcje nie są skalowalne. Strumień zdarzeń tylko do dołączania (magazyn zdarzeń) jest matką skalowalności.
Drugim powodem jest enkapsulacja zagregowana. Musisz go chronić. Oznacza to, że agregat powinien mieć swobodę zmiany wewnętrznej reprezentacji w dowolnym momencie. Jeśli go przechowujesz i mocno na nim polegasz, będziesz miał trudności z wersjonowaniem, co na pewno się wydarzy. W sytuacji, gdy używasz migawki tylko do optymalizacji, kiedy zmiany schematu po prostu ignorujesz te migawki ( po prostu ? Nie wydaje mi się, że tak; powodzenia w ustaleniu, że schemat agregacji zmienia się - w tym wszystkie zagnieżdżone elementy i obiekty wartości - w efektywny sposób i zarządzanie tym).
źródło