Koncepcja CQRS (Command Query Responsibility Segregation) jest dla mnie nowością. W teorii oczywiście spotykałem się już z nią wcześniej czytając artykuły na blogach czy też oglądając prelekcje znalezione w Internecie. Jednak nie miałem okazji, aby zobaczyć i zastosować ją w praktyce. Postanowiłem, więc skorzystać z CQRS w aplikacji AnimalShelter, którą rozwijam w ramach serii “Przepisz swój kod na nowo”. Z tego powodu w tym wpisie przedstawię CQRS okiem laika, czego ja się dowiedziałem na jej temat, aby łatwiej rozpocząć implementację.

Co mówi Internet na temat CQRS?

Znany i lubiany portal Baeldung przedstawia CQRS jako rozdzielenie komend i zapytań w architekturze aplikacji. Opisuje również, że CQRS opiera się na zasadzie Command Query Separation (CQS), która została zasugerowana przez Bertranda Meyera polegająca na podziale operacji na obiektach domenowych na dwie kategorie: Queries (zapytania) i Commands (komendy).

Koncepcja CQS
Źródło: https://www.baeldung.com/cqrs-event-sourcing-java

Zapytania służą do zwracania rezultatów. Nie zmieniają one stanu aplikacji. Wysyłane są tylko po to, aby otrzymać niezbędne dane niczym SELECT w SQL. Natomiast komendy są przeciwieństwem zapytań. Ich głównym zadaniem jest zmiana stanu systemu i niekoniecznie muszą one zwracać rezultat. Ten efekt możemy osiągnąć tylko wtedy jeżeli podzielimy model domeny na dwa modele: Commands i Queries.

Jak widać na wyżej przedstawionym obrazku istnieje możliwość pójścia nawet o krok dalej. Można rozdzielić warstwę persystencji na dwie części: zapis oraz odczyt. Natomiast wtedy trzeba zapewnić mechanizm synchronizacji pomiędzy tymi dwoma źródłami danych.

Na Baeldung (bądź na GitHub) możemy zobaczyć przykład zastosowania CQRS. Pokazany jest nawet sposób synchronizacja danych, więc naprawdę polecam tam zajrzeć jeśli chciałbyś lub chciałabyś zobaczyć koncepcję CQRS w praktyce.

Informacje z DevStyle o CQRS

Maciej Aniserowicz w listopadzie 2016 roku prowadził akcję BLOGvember w ramach, której opisywał swoje postrzeganie CQRS. W jednym z artkułów zamieścił esencję tej koncepcji: “Samo serce CQRS to jedno proste przykazanie: oddziel zapis od odczytu. Tylko tyle i aż tyle.”. Właśnie, należy podzielić aplikację na dwie części, czego następstwem jest utworzenie dwóch modeli: jeden przystosowany do odczytu, drugi do zapisu. Dostajemy, więc to samo co od Baeldung. Kwestią otwartą pozostaje jeszcze implementacja tego rozwiązania. Maciek przedstawił swój sposób stworzenia szkieletu CQRS pod tym artykułem. Warto się z nim zapoznać, bo to rozwiązanie powstało na bazie doświadczenia niezłego programisty. Chociaż jest ono napisane w C# to jest na tyle proste, że każdy Java developer powinien je zrozumieć :)

CQRS... to dziecinie proste!
CQRS wydaje się być… dziecinie prosty :) Źródło: http://devstyle.pl/2016/11/23/esencja-cqrs-to-bardzo-proste

Do rozważenia zostaje jeszcze sprawa czy Command powinien zwracać rezultat. Na devstyle.pl dowiadujemy się, że “komenda=void”. Jeżeli coś się nie powiedzie np. walidacja danych albo system nie będzie działał prawidłowo to otrzymamy… WYJĄTEK! Po to właśnie one są, dają nam informację zwrotną, że coś poszło nie tak. Właśnie to uzasadnienie pozwala nam na to, żeby komenda nic nie zwracała. Jeżeli wszystko pójdzie prawidłowo (Happy Path) i nie otrzymamy wyjątku to znaczy, że zapis odbył się bez najmniejszych problemów. Tylko i aż tyle.

Podsumowanie

Koncepcja CQRS wydaje się naprawdę prosta i ma na pewno sporo zalet. Pozwala zastosować wydajniejsze bazy danych dla wybranych typów operacji np. dla zapisu relacyjną bazę danych, a dla odczytu noSQL. Odczyty wydają się być częstszą operacją, więc zyskujemy możliwość skalowania tej części aplikacji odpowiedzialnej właśnie za zapytania. Jednak jak wszystko i CQRS ma też ciemne strony. Na pewno trzeba mieć szerszą wiedzę ekspercką w technologiach bazodanowych. Używanie wielu baz danych dodaje nam kolejne punkty zapalne, więc należy zadbać o odpowiedni system monitorujący aplikację oraz o mechanizmy ratujące, gdy “coś pójdzie nie tak”.

W AnimalShelter spróbuję zastosować CQRS na podstawie wiedzy jaką wyniosłem z wyżej przedstawionych artykułów. Oprę się o koncepcję, żeby Command zwracał void i zobaczę jakie niesie to ze sobą konsekwencje. Także pora zakasać rękawy i brać się do roboty!