FULL PRO CQRS - czy w tym przypadku warto i pytania

0

Cześć. Wyobraźmy sobie, że mamy dość sporą apkę mikroserwisową z ładnie odseparowanymi DB z PGSQL (uwzględniłem to, bo pracowałem w banku, gdzie były MSy na wspólnej DB i fajnie, łatwo robiło się CQRS.. oczywiście nie zważając na wady wspólnej bazy w tym przypadku).

Żeby nie zagłębiać się super w domenę tej apki na warsztat weźmy trzy mikroserwisy - item , customer oraz search. Item to jakiś tam produkt w sklepie, customer to wiadomo - kupujący lub sprzedający, a search to mikroserwis nasłuchujący na zdarzenia domenowe i zapisujący sobie dane w elasticsearchu, które potem łatwo można szukać poprzez jakieś filtrowania.

Mamy sobie dwa ekrany na UI:

  1. Lista produktów z różnymi filtrami szukającymi
  2. Ekran szczegółów produktu

Póki co agregowanie danych na tych ekranach jest rozwiązane w ten sposób:

  1. Na podstawie filtrów idzie request do search service, który przeszukuje sobie elasticsearcha i zwraca listę IDków itemów (mimo, że ma część danych każdego itemu na potrzeby filtrowania), a następnie idzie drugi request już do item service z tymi id po listę itemów z większą ilością danych.

  2. Tutaj wejściem jest id itemu i następuje ok 5 zapytań do kilku różnych mikroserwisów po potrzebne dane. Czyli najpierw idzie request po detale itemu, następnie idzie request po detale sprzedawcy (jak już znamy sellerId z pierwszego requestu) i potem kaskadowo po inne potrzebne dane (niektóre oczywiście idą równolegle). Generalnie logika tam na froncie jest dość skomplikowana - po prostu front jest agregatem danych.

Czy na te dwa ekrany (i wiele innych w systemie gdzie agregujemy dane z wielu MS) CQRS byłbym dobrym rozwiązaniem?

  1. Sprawę z search service można załatwić w trochę odwrotny sposób - mógłby nasłuchiwać na dedykowany dla siebie rozszerzony event ze WSZYSTKIMI danymi danego itemu. Wtedy nie musimy uderzać z listą id do item service tylko po prostu search service jest naszym wejściem do części query.

  2. Wykorzystać search service, utworzyć w nim jakiś read model typu ItemDetails zawierający wszystkie dane potrzebne na tamtym ekranie i potworzyć nowe dedykowane eventy typu UserUpdated, AddressUpdated. Wtedy na ekranie Item Details nie musimy robić 5 requestów po dane tylko jeden do tego search service.

Jakie mam wątpliwości i pytania..

  1. Już teraz istnieje mi w systemie event ItemAdded/ItemUpdated z biznesowymi danymi, na które nasłuchują inne biznesowe MSy. Jeśli miałbym dołożyć nowy event emitowany przy dodaniu, edycji itemu to powinienem rozszerzyć ten istniejący czy dodać zupełnie nowy i emitować po prostu dwa eventy? Oczywiście pytam tu nie tylko o kontekst itemu, ale o resztę, którą będzie ten search używał do agregowania danych.

  2. Jak zasilić search service o istniejące dane po wprowadzeniu featura aby doprowadzić do jego spójności z resztą? No bo teraz on np. nie słucha na Usera, a musiałby,

  3. Jakie są strategie sprawdzania spójności i naprawiania ewentualnych braków? Zawsze event może nie dojść, albo coś innego może się sypnąć.

  4. Czy to jest w ogóle dobry pomysł?

0

Ktoś, coś ? ;p

Na tematy architektury chyba najczęściej wypowiada się @Charles_Ray

Zastanawiam się jak najlepiej przeprowadzić taką migrację.

  1. Dać search servicowi dostęp read-only do baz danych potrzebnych MSów i zrobić migracje na poziomie skryptów.

czy też

  1. Po starcie Search Service sprawdzić stan elastica i jak będzie pusty to wysłać jakiś event w stylu dbread.initialized i wtedy wszystkie MSy powysyłają techniczne eventy w stylu ItemMigrationEvent, UserMigrationEvent. Boję się trochę o wydajność i zastanawiam się czy tą migrację robić wtedy jakoś batchowo czy jeden event na rekord czy też w jednym evencie wszystkie rekordy.
0
  1. Przed zapisaniem dokumentu w Elasticu możesz w serwisie dociągnąć synchronicznie niezbędne dane. Czyli reagujesz na event, że dodano item i sobie dociągasz usera albo dane z innych domen.
  2. Nie rozumiem koncepcji z tymi migracjami, chodzi o inicjalizację na start wyszukiwarki? Jeśli chodzi o utrzymanie spójności (eventual consistency), to nie wiem jak to zrobić perfekcyjnie, ale może w 90% przypadków wystarczy monitoring laga na kolejce. W przypadku, kiedy nie uda się dociągnąć danych (np. tego usera) można albo odbić cały event ItemAdded, albo ponawiać pobieranie usera.
0

Tak, chodzi o inicjacie wyszukiwarka na start. No bo jakieś dane już w tabelkach mam i muszę jakoś wystartować tega elastica z danymi.

0

Ile tych danych? Może przerzucić jednorazowo skryptem? Wyemitować sztucznie eventy? Trzeba tylko uważać, żeby nie dać zbyt dużego ruchu jeśli to prod.

0

Powiedzmy, że z 10 tabel po 10 tys rekordów (i tak dużo zawyżyłem). To na razie staging, proda jeszcze nie ma. Mogę to opędzlować batchowo zawsze,

Miałem pomysł na taki mechaizm, aby w każdym mikroserwisie, który emituje eventy zrobić tabelkę SYNCHRONIZING_COMMAND, która będzie zawierała takie dane, jak nazwa tabeli, min id, max id i confirmed i zdefiniować joba, która sobie chodzi co jakiś tam czas i sprawdza czy jest coś do zasilenia. Załóżmy, że chcemy zaktualizować Searcha wszystkimi danymi z tabeli item to po prostu:

Insertujemy do DB wiersz z table_name = ITEM, min id = null, max id = null, confirmed = false. Nulle oznaczają, że nie ma granic po obu stronach więc ściągamy wsio. Emitujemy migracyjne eventy i zapisujemy confirmed na true. Ta tabelka byłaby po prostu odpowiedzialna za trzymanie poleceń migracji.

Dzięki takiemu mechanizmowi możemy w trakcie działania apki naprawiać jak jakieś dane przestaną być z jakiegoś powodu spójne.

O przerzuceniu skryptem też myślałem, żeby użyć jakiegoś logstasha do tego.

1

Ciekawe, nie wiem jakby to rozwiązanie się skalowało względem liczby usług oraz jaki byłby narzut na development i utrzymanie tego. Jeśli to jest jednorazowa akcja, to zrobiłbym to najprościej jak się da, czyli skrypt :) przy takiej ilości danych to w ogóle nie widzę komplikacji, to nie są TB danych

0

W jakim sensie skalowało względem lizcby usług?
Jestem świadomy tego, że na każdy Mikroserwis wymagałby stworzenia takiej tabeli, napisania tego joba, emisji eventów itd. Chociaż no to nie jest też aż tak sporo pisania kodu.

0

Może to czego szukasz to wzorzec Transactional Outbox. Pamiętaj ze tabela to baza, a baza dodaje złożoności do aplikacji, bo to jest osobny komponent który trzeba ogarniać. Nie wiem czy podejście „nowa usługa - nowa tabela” nie jest przeinżyniorowaniem, które potem będzie uwierać jak kamień w bucie. Może problem ma charakter bardziej lokalny?

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