Dlaczego konto bankowe nie jest najlepszym przykładem Event Sourcingu
Cześć!
Nie dowiozłem! To dopiero drugi newsletter, a ja się spóźniłem. Miało wyjść słowo na niedzielę, a wyszedł witaj nowy tygodniu. Dlaczego? Bo dowoziłem coś innego - pokój z tapetą w Alpaki dla małej obywatelki, która do dwóch tygodni się do mnie wprowadzi. Swoją drogą jeżeli ktoś wymyśli aerobik gdzie będzie się chodziło po drabinie w górę i w dół z wiaderkiem farby i machało rękami z pędzlem w ręku - to pamiętaj, ja wymyśliłem go pierwszy. Ostrzegam, bardziej męczące niż Cross Fit!
No ale dobra, do sedna!
Dlaczego twierdzę, że konto bankowe nie jest najlepszym przykładem Event Sourcingu? Zacznijmy na przekór, powiedzmy dlaczego jest.
Nazwa Event Sourcing wprost oznacza, że źródłem (source) prawdy są zdarzenia (events). Zapisujemy stan i historię aplikacji jako serię następujących po sobie zdarzeń. Czyli zachowując i modyfikując aktualny stan naszego systemu logujemy darzenia w nim zachodzące. To tak jak opisując komuś nasz dzień mówimy: “No wiesz, tramwaj mi się spóźnił, przez to byłem później w robocie. Przez to musiałem dłużej zostać i spóźniłem się na spotkanie z Tobą”. Rzadko przecież mówimy “spóźniłem się to się spóźniłem, po co drążyć temat”, wymówka musi być. Podobnie w systemach biznesowych zwykle okazuje się, że możliwość podrążenia tematu jest czymś co biznes od nas oczekuje. Często też jedną z głównych wartości naszego systemu.
Tak właśnie jest w systemie bankowym. Zapisujemy w nim wszystkie transakcje dla danego konta. Wszystkie nasze wpływy (np. pensje, wpłaty itd.) oraz wydatki (np. wypłaty karta, opłaty itd.). Każda z tych transakcji:
- niesie konkretną biznesową informację,
- zachodzi w konkretnych okresie czasu,
- następuje jedna po drugim,
- jest niezmienialna (nie możemy cofnąć przelewu jak już został dokonany).
Czyli wszystko jest ok, czemu się czepiam? Szczególnie, że sam nie jestem święty, bo też podaję przykład: https://github.com/oskardudycz/EventSourcing.NetCore/tree/main/Sample/BankAccounts.
Ten przykład jest problematyczny z tego powodu, że nie jest typowym przykładem problemu w Event Sourcing. Przez to łatwo zarzucić mu problemy wydajnościowe i zrobić generalizację, że cały Event Sourcing jest mało wydajny. Zróbmy małą kalkulację. Powiedzmy, że dziennie robimy ze 3 transakcje, mleko w sklepie, fajki w kiosku, przelew od cioci. 3 razy 365 = 1095. Tyle zdarzeń przybędzie nam rocznie. Ja konto, które założyłem w wieku 18 lat mam do dzisiaj, 16 lat prawie. 16 x 1095 = 5840 plus 4 dni za lata przestępne. Po co ta matematyka? W Event Sourcingu stan naszej aplikacji (modelu/encji/agregatu) poprzez zaaplikowanie zdarzeń jeden po drugim. Czyli dla konta bankowego jak zarobiliśmy 1000zł to dodajemy je do stanu konta, jak wypłaciliśmy 257zł to odejmujemy, otrzymując ostateczny stan konta równy 743zł. Biorąc przykład mojego konta musielibyśmy pobrać 5840 zdarzeń a następnie po kolei je zaaplikować? Szaleństwo! To nie może być wydajne i nie jest!
Normalnie obiekty w naszych systemach nie mają tyle zdarzeń i nie żyją tak długo. Zgłoszenie serwisowe - 2 tygodnie, kilka zmian (odebrano, zweryfikowano, oceniono, zamknięto). Dostawa towaru, 2-3 dni i kilka zmian (wysłano, przekazano kurierowi, przewieziono do bazy, przesłano do innego miasta, itd.). Normalnie pobranie kilku prostych zdarzeń (nawet 20) to nie jest wielki problem, większość aplikacji nie ma tak wyśrubowanych wymagań wydajnościowych tak aby było to problemem.
Jeśli jednak jest to zwykle rozwiązaniem są snapshoty. Co to jest snapshot? Jest to dosłownie “stopklatka”, czyli zrzut stanu naszego modelu w danym okresie czasu, np.:
- aktualny stan obiektu - może być zapisany np. w znanej nam i lubianej tabeli relacyjnej, gdzie każde pole obiektu to osobna kolumna. Może również być zapisane w postaci klucz wartość, gdzie kluczem jest identyfikator encji, a wartością jej stan zapisany w JSON, a potem siup do tabeli - tak to robimy w Marten - zobacz więcej w http://jasperfx.github.io/marten/documentation/events/projections/.
- stan na dany moment czasu, np. stan konta na początek miesiąca, możemy wtedy pobrać np. snapshot z początku miesiąca, plus zdarzenia, które wydarzyły się później i je zaaplikować,
- stan po kazdej transakcji - wtedy otrzymujemy z automatu historię i wszystkie stany naszego obiektu.
Inną opcją jest wysłanie zdarzenia “podsumowującego”, które prześle stan obiektu na dany moment czasu. Co to w praktyce oznacza? Nawet w systemach finansowych dane są trzymane z jakąś kadencją, zwykle interesuje takie systemy konkretny przedział czasowy - np. okres rozliczeniowy. Nawet dane finansowe nie muszą być trzymane wiecznie, faktury o ile się nie mylę w Polsce 5 lat. Możemy więc wysłać zdarzenie typu “ZakonczonoRokFinansowy” dla naszego konta, ktore bedzie miało aktualny jego stan i ogolnie informacje, a stare zdarzenia zarchiwizować (np. przenieść do innej bazy z której można zobaczyć pełną historię na żądanie). Dzięki temu mamy pełnię informacji i możemy ciągle zachować zalety Event Sourcingu. Zerknij też na świetny opis Mathiasa Verraesa o wzorcu “Summary Event” - http://verraes.net/2019/05/patterns-for-decoupling-distsys-summary-event/. Zapraszam też do przerobienia zadań z mojego “Self-paced kit” https://github.com/oskardudycz/EventSourcing.NetCore/tree/master/Workshop/01-EventStoreBasics.
To tylko dwa z potencjalnych rozwiązań, ale już to może oszołomić i sprawić, że Event Sourcing wyda się trudniejszy niż jest w rzeczywistości. Co mogłoby być lepszym przyładem? Choćby równie ograny przykład Zamówienia czy też wspomniany już przeze mnie przykład zgłoszenia serwisowego. Innym przykładem to meetup grupy programistycznej. To są rzeczy, które są bliższe regularnym problemom rozwiąznywanym na codzień w Event Sourcing. One wcale nie odbiegają niczym od tego co jest chlebem powszednim programisty. To co różni Event Sourcing od tradycyjnego programowania jest to co powiedział Greg Young:
“When you start modelling events, it forces you to think about the behaviour of the system. As opposed to thinking about the structure of the system.”
Znasz lepsze przykłady? A może znasz jeszcze gorsze? Napisz. Nie zgadzasz się? Również. Chętnie podyskutuję.
Ale teraz już kończę, moja przyszła współlokatorka poza tapetą w Alpaki potrzebuje jeszcze skręconego łózeczka. Priorytety!
Pozdrawiam serdecznie i do napisania.
Oskar
p.s. czekam na pytania/sugestie/uwagi jak zrobić ten Newsletter interesującym i lepszym dla Ciebie. Daj znać!