devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
4 minut

Esencja CQRS – to bardzo proste


23.11.2016

CQRS (Command Query Responsibility Segregation) ma wiele odmian. Jeden napisze tak, drugi napisze inaczej. I FIGHT – święta wojna gotowa. O podstawach CQRS, z odrobiną historii i przykładami, wkrótce jeszcze napiszę. Dziś: o esencji, o “corze”, o serduszku <3.

Akcja: BLOGvember! Post nr 18.
W listopadzie w każdy roboczy poranek na devstyle.pl znajdziesz nowy, świeżutki tekst. W sam raz do porannej kawy na dobry początek dnia.
Miłej lektury i do przeczytania jutro! :)

O co spór?

Różnice w implementacji podejścia CQRS zaobserwować można na każdym kroku. Każdy programista ma swoje racje i nie ma jednej poprawnej drogi. Jak to zwykle bywa: “whatever works“. Z dopiskiem: “in a given context“.

Niedawno zaprezentowałem na blogu swoje podejście do składowych komponentów CQRS i ich rejestracji w kontenerze DI. Niedługo pojawi się kolejny, dłuższy, tekst na podobny temat. Można tam poczytać o takim tworze jak ICommandBus – komponencie, którego rolą jest dystrybucja wiadomości w danym systemie. Dla mnie to piękna koncepcja, niosąca bardzo wiele korzyści i praktycznie pozbawiona wad. Znajdą się jednak zwolennicy teorii, że czegoś takiego jak “szyna komend” w ogóle nie powinno się implementować. I git, nie ma się o co bić.

W moich projektach, na moje potrzeby, w MOIM KONTEKŚCIE, zrównoleglanie operacji najczęściej nie było potrzebne. Wyznaję zasadę, aby wszystko było tak proste jak to tylko możliwe. Jeśli da się więc osiągnąć założenia biznesowe bez wielowątkowości, komunikacji między procesami czy asynchroniczności, to tak właśnie zrobię. Można jednak natknąć się stwierdzenia, że “implementowanie CQRS na jednym wątku jest bez sensu“. Hmm… U mnie tak to właśnie działało: komenda -> handler -> event -> handler -> event -> handler – wszystko na jednym wątku, bez zrównoleglania. I sprawdziło się wyśmienicie.

Wreszcie: “jakie dane powinny zwracać komendy?“. W MOIM podejściu: żadnych (o tym też się jeszcze rozpiszę). Komenda to VOID, i już. Ileż razy byłem jednak przekonywany, że to bardzo ograniczające i nieprawidłowe podejście…

Wiesz co? Wszystkie powyższe spory to tylko szczegóły implementacyjne.

Esencja CQRS

Samo serce CQRS to jedno proste przykazanie: oddziel zapis od odczytu. Tylko tyle i aż tyle.

CQRS = oddzielenie zapisu od odczytu

Jeśli masz problemy opisane powyżej, to jest już dobrze. To są problemy, które fajnie jest mieć. Przynajmniej w porównaniu z alternatywą.

W CQRS bierzemy to:

clip_image001

Czyli JEDEN MODEL, służący do wszystkiego. I do zapytań, i do wykonywania logiki, i do reprezentacji danych. Duży zestaw skomplikowanych klas. Zestaw, który się nie sprawdza.

Zamieniamy to na:

clip_image002

Uświadamiamy sobie, że potrzebujemy więcej niż jeden model. Potrzebujemy przynajmniej dwa modele: jeden przystosowany do odczytu danych i jeden dedykowany do przetwarzania/zapisu.

Jak je zaimplementujemy? To kwestia drugorzędna. Ja mam swój sposób, Ty możesz mieć inny sposób. Po takiej operacji i tak jesteśmy w o wiele lepszej pozycji, niż jeszcze niedawno.

Bo:

Asking a question should not change the answer
Bertrand Meyer

BUM. Koniec.

Bonus 1

O CQRS możesz posłuchać w moim podkaście nagranym z jednym z dwóch “ojców” tego podejścia: DevTalk#14 – CQRS with Udi Dahan. Bardzo ciekawa audycja w kontekście mniej technicznym, takie treści trudno znaleźć więc bardzo polecam.

Bonus 2

Dlaczego ten obrazek pasuje do dywagacji o CQRS? :)

clip_image003

Nie przegap kolejnych postów!

Dołącz do ponad 9000 programistów w devstyle newsletter!

Tym samym wyrażasz zgodę na otrzymanie informacji marketingowych z devstyle.pl (doh...). Powered by ConvertKit
Notify of
maciek

Policjantom się nie spodobało, bo to wyjaśniłeś za szybko! W CQRS ma być powoli, trudno i skomplikowanie! Panów policjantów nie interesuje czy dowieziesz paczke do klienta, panów policjantów interesuje czy twoja komenda zwraca void.

A tyś Pan to na dziecięcej zabawce rozrysował:)

Piotrek

Policjanci zawsze chodzą parami, bo jeden umie czytać a drugi pisać ;)

maciek

A to też, też:)

jedmac
jedmac

Bo jeden czyta, a drugi pisze :P

Dariusz Pawlukiewicz

Ja bym jeszcze dodał w ramach tej esencji, że implikacją takiego podziału jest możliwość skalowania asymetrycznego aplikacji oraz doboru technologii/struktur danych pod operacje zapisu i odczytu. Zgadzam się też, że trwa od dawna wojna o “poprawną” implementację tego wzorca. “ooooo aaaa CommandBus to antywzorzec!”, “lol, CommandHandler zwraca VOID? To jak pobiore ID nowo utworzonego elementu? to bez sensu i tylko komplikujesz sobie życie”. Najlepsze jest, że potem wiele osób w pewnym momencie nieświadomie zaczyna implementować CQRS wydzielają komponenty pod zapis i odczyt, ponieważ jest to wygodne i zazwyczaj nie ma problemu ze znalezieniem tej granicy pomiędzy read i write. Anyway,… Read more »

devamator
devamator

W temacie ID można by się pewnie sporo rozpisywać, ale dla mnie najważniejszym aspektem jest, że ID to dana czysto techniczna.
Warstwa dostępu do danych jak najbardziej z niej korzysta i tam IDiki latają tam i z powrotem.
Według mnie CQRS w swoim założeniu lata pomiędzy warstwą interfejsu (UI,WCF,Bus,itp…), a na tej warstwie powinny latać identyfikatory bardziej biznesowe (np.: numer faktury a nie jej id).
A skoro do Command trafił na wejściu identyfikator biznesowy, to po co go przesyłać dalej, skoro w kontekście wywołania swojej komendy już go masz.

RG
RG

Było by łatwiej gdyby nie to że ktoś kiedyś wymyślił formularze html-owe które standardowo robią post-a na ten sam adres z którego przyszły – czyli wyświetlasz stronę z formularzem no więc ta strona powinna też obsługiwać zapis danych z tego formularza. No i jakoś mnóstwo ludzi się do tego przywiązało bo wygodne i intuicyjne. No i jak tu przylepić do tego CQRS bez mieszania wszystkiego ze wszystkim?

Dariusz Pawlukiewicz

No, ale GET i POST to na serwerze dwie oddzielne akcje kontrolera. CQRS w zasadzie nie dotyczy warstwy HTTP, tylko wszystkiego co poniżej. W związku z tym Twój GET w kontrolerze będzie wołał read model z części aplikacji do odczytu, natomiast POST będzie przekazywał command dalej do ComamndBusa czy bezpośrednio do CommandHandlera. Nie ma tu żadnego problemu.

RG
RG

Tyle że w tak oczywistym miejscu musisz CQRSa wepchnąć gdzieś w niższe warstwy, bo Twój MVC kontroler obsługuje zarówno odczyt jak i zapis w GUI (a często jeden POST jednocześnie zapisuje dane i generuje widok w HTMLu). Czyli jak na moje oko oczywista skucha tego obiecującego modelu – zamiast rozwiązać problem omijamy go przez wrapowanie, abstrakcję i schowanie gdzieś głębiej.

RG
RG

“Whatever works” – każdy stosuje, w końcu chodzi o to żeby było łatwo i prosto, a jak się przy okazji da dobrze to super. I raczej wszystko jest OK z kontrolerem bo to standardowa praktyka żeby w jednym requeście POST zapisać dane a potem wygenerować view. Bo po co robić dwa requesty do serwera jeśli można jeden?. Ale to też oznacza że zapis i odczyt następują w jednej transakcji więc separacja jednego od drugiego jest tylko logiczna – można sobie oddzielić kod z tych obszarów i MVC to robi. Nadal jednak view musi czekać aż dane zostaną zapisane. tak więc… Read more »

RG
RG

czyżby początki świętej wojny? :) Myślę że obsługa transakcji to ważny temat i nie da się go obejść ani zignorować – w końcu wyjdzie i okaże się że ta separacja odczytu od zapisu to coś trudniejszego niż tylko organizacja kodu, a jeśli oddzielisz od siebie te operacje do niezależnych transakcji to napotkasz problemy ze współbieżnością / synchronizacją operacji. Co do Post/Redirect/Get – to już ma znamiona dogmatu, w końcu niezależnie od typu requestu wysyłasz dokładnie ten sam pakiet danych. To nie dotyka istoty problemu i samo majstrowanie przy urlach go nie usuwa. PS nie żebym się czepiał, mam jakieś swoje… Read more »

devamator
devamator

Widzę, że pożyczyłeś profesjonalny tablet do rysowania diagramów :)

A co do wpisu, to CQRS jest jednym z wielu możliwych podejść zapewniających SOLID na poziomie architektury projektu.

PaSkol

Poczekam aż dojdziesz (he, he :) ) do kwestii komend i ich uchwytu. Być może wdam się w polemikę.

MaciejBurchardt
MaciejBurchardt

Co do Bonusu numer 2 -> Obrazek pasuje bo nie pisze się wpisów na bloga o CQRS prowadząc samochód? ;)

Keraxel

Kiedyś zrobię sobie tatuaż. Na jednym przedramieniu będzie “whatever works”, a na drugim “in a given context”. Dzięki za inspirację!

Moja książka

Facebook

Zobacz również