Raportowanie a CQRS
Zdaję sobie sprawę, że tytuł jest ogromnym skrótem myślowym. Tak naprawdę chodzi mi o możliwość generowania raportów z rozwiązań wykorzystujących model domeny z CQRS. Oczywiście, posłużę się przykładem DDDSample. Na wstępnie jednak muszę się przyznać, że nie jestem ekspertem od business intelligence, więc jeśli popełniłem jakieś karygodne wykroczenia, proszę o wyrozumiałość i zgłoszenie poprawek w komentarzach — będę wdzięczny za wszelkie uwagi.
Problem wygląda następująco: nasz system transakcyjny działa świetnie, jest wydajny, bezpieczny itp. Nadszedł jednak czas, aby wygenerować z niego jakiś raport potwierdzający, że system się sprawdza biznesowo. Jak to zrobić, mając po stronie komend silnie znormalizowaną bazę zoptymalizowaną dla operacji update/insert, a po stronie zapytań — tabele odzwierciedlające formatki UI? Są dwie możliwości:
- Zmodyfikować bazę strony zapytań, aby stała się prawdziwą “bazą raportową”. Dzięki temu będzie mogła służyć i do generowania raportów i do wyświetlania danych na formatki. Niestety konsekwencją tego jest znaczne zwiększenie skomplikowania kodu dostępu do danych dla formularzy i list. Dlaczego? Zaraz zobaczymy.
- Dodać kolejną (3!) bazę danych zoptymalizowaną pod kątem raportów. Minusem jest, jak zwykle, zwiększony wysiłek ze strony administratorów. Plus (również jak zwykle): lepszy podział odpowiedzialności i możliwość optymalizacji.
Spróbujmy więc zrealizować to drugie rozwiązanie. Na początek potrzebujemy struktury bazodanowej, która nadawałaby się do generowania raportów. Oczywiście, w rzeczywistej sytuacji zapytalibyśmy naszego klienta, jakich raportów potrzebuje. Niestety nie mam takiej możliwości pisząc te notkę. Przygotowałem więc schemat, który wydaje mi się użyteczny:
Pozwala on na analizę towarów pod kątem zmian rzeczywistej trasy w stosunku do tej zarejestrowanej i ich wpływu na dostarczenie towaru. Z drugiej strony umożliwia także analizę samych tras: którędy prowadziła, czy wynikiem było złe skierowanie towaru, czy została porzucona (zmieniona), czy też w wyniku jej realizacji towar został dostarczony.
Na pierwszy rzut oka widać, że transformacja tak przechowywanych danych do formatu niezbędnego do wyświetlenie na formatkach byłoby bardzo trudna. Utwierdza mnie to w przekonaniu, że (tym razem) słusznie wybrałem wariant drugi (osobne bazy).
Mając już strukturę danych, zastanówmy się, czego potrzebujemy, aby ją zasilić? Z pomocą przychodzi nam naturalna właściwość tego rodzaj systemów CQRS. Są one naturalnie przystosowane do scenariuszy real-time business intelligence, ponieważ posiadają wbudowany mechanizm wypychania danych z bazy transakcyjnej w czasie rzeczywistym (służący do zasilania bazy dla zapytań). Możemy się więc w niego bezwstydnie wpiąć. Potrzebujemy jedynie czterech obiektów obsługi komunikatów dla zdarzeń:
- zarejestrowano towar (utworzenie obiektu w CargoFacts i RouteFacts)
- przypisano trasę (aktualizacja RouteFacts)
- zmieniono lokalizację docelową (utworzenie obiektu w RouteFacts, modyfikacja reprezentującego poprzednią trasę)
- zarejestrowano zdarzenie obsługi (aktualizacja rzeczywistego miejsca załadunku towaru i miejsca dostarczenia towaru, aktualizacja pola “misdirected” itp.)
Implementacje możemy wykonać (znów) na dwa sposoby: albo bezpośrednio przygotowując odpowiednie komendy SQL, albo mapując przedstawiony model na obiekty. Co kto woli.
PS. Zamierzam spróbować zaimplementować przedstawione rozwiązanie. Jak tylko mi się uda, podzielę się z Wami wynikami eksperymentu.





about 5 months ago
Czekałem na bardziej odpowiedni post z zadaniem tego pytania, ale jednak zadam teraz:). Odnośnie fragmentu:
“posiadają wbudowany mechanizm wypychania danych z bazy transakcyjnej w czasie rzeczywistym (służący do zasilania bazy dla zapytań)”
No właśnie… jak to się ma do wydajności względem “tradycyjnego” podejścia? Normalnie operacje zapisu/odczytu przeplatają się, a baza “zapytaniowa” teoretycznie ma być szybsza, ponieważ jest tylko do odczytu. Ale przecież nie jest, ponieważ non stop zachodzą w niej takie same zmiany jak w bazie transakcyjnej. Może nawet zachodzi w niej zmian wynikających z jej zdenormalizowanej natury.
Widziałem rozwiązania “optymalizujące” pchające zmiany do bazy raportowej w paczkach jedynie wtedy, gdy przychodzi jakieś żądanie tak aby niepotrzebnie tej bazy nie aktualizować. Ale wydaje mi się to trochę bez sensu – po co wstrzymywać się z aktualizacją bazy do momentu, w którym będzie obciążona zapytaniami?
Czy masz jakieś przemyślenia dotyczące właśnie tego tematu – aktualizacji bazy raportowej/zapytaniowej, ew. różnych podejść do tego problemu?
about 5 months ago
Jedyne czego sie obawiam oprocz problemu ktory poruszyl Procent w swoim komentarzu jest takze strafa techniczna ktora ma zajac sie aktualizacja bazy raportowej. W przypadku bardziej skomplikowanych aplikacji mapping pomiedzy baza do zapytan i baza raportowa moze byc bardzo skomplikowany.
Z drugiej strony patrzac na to ze czesto generowanie raportow moze byc traktowane jako inny projekt lub podczesc projektu, dodatkowa praca nad tym aspektem wcale mnie nie przeraza.
Mysle jednak ze trzecia baza bylaby najlepszym rozwiazaniem.
about 5 months ago
– uwaga, jezeli dobrze zrozumialem tresc art
to komentarz jest poprawny
jezeli nie to sorki
Jezeli chodzi o trzecia baze. IMO do raportow (w sensie prawdziwym) przewaznie powinno sie tworzy baze agregujaca dane na scisle okreslonych zasadach – jak te dane beda agregowane i kiedy to zalezy juz od nas.
@Procent
baza raportowa to nie to samo co baza zwykla. Tutaj istnieje sens pchania danych wtedy kiedy potrzebne (oczywiscie i bez tego co jakis czas ta baza powinna sie aktualizowac). Dodatkowo przewaznie raporty nie stanowia Widoku na dane w bazie, ale jakies konkretne agregacje danych. W tym wypadku moga one znaczaco obciazyc serwer bazodanowy.
w zaleznosci od projektu albo stosowalem kostki albo osobna baze. sposob aktualizacji bazy raportowej zalezal stricte od ograniczenia wybranego rodzaju rozwiazania.
@Procent i @Szymon
uwazajcie ze slowem normalizacja w bazach danych
about 5 months ago
Jak już pisałem, nie jestem ekspertem od BI, ale moje dotychczasowe doświadczenie z systemami OLTP pozwala mi stwierdzić, że gdybym był odpowiedzialny za budowę nowego rozwiązania na pewno oddzieliłbym funkcjonalność raportową do osobnej bazy. Po prostu zbyt wiele problemów powoduje uruchamianie na bazie transakcyjnej bardzo skomplikowanych zapytań dla raportów.
Kwestia, jak często ją aktualizować jest interesująca i zapewne zależy od potrzeb biznesu. W pewnych przypadkach wystarczy raz dziennie i moje rozwiązanie z CQRS byłoby przesadą. W innych natomiast ważne jest, aby raporty agregujące dane były aktualne co do minut.
@Procent
Dlaczego baza do zapytań jest szybsza?
i zapytania na potrzeby list/szczegółów mają mniej joinów
1. Bo jest zdenormalizowana
2. Bo można uruchomić dowolną ilość serwerów bazodanowych hostujących bazę do odczytu i w ten sposób skalować tę warstwę niemal liniowo
@Gutek
Co jest nie tak z tą normalizacją? Czegoś nie zajarzyłem?