PictOgr – mój CQRS -2-

Command Query Responsibility Segregation – 2 –

CQRSPo omówieniu komend pora na przejście do zapytań. Ich celem jest odczytywanie danych i z wracanie w odpowiedniej do wymagania formie.

Do wykonywania zapytań posłuży szyna zapytań. Dzięki jej zastosowaniu wywołanie zapytania odbywać się może w dowolnym miejscu aplikacji ze wstrzykniętą odpowiednią zależnością.

Wykonanie handlera zapytania odbędzie się zawsze w tym samym środowisku (szyna), zawsze będzie opakowane tym samym algorytmem i nie trzeba tutaj się martwić np. o stosowanie wyjątków, logi, gdyż w trakcie wykonywania przez szynę zapytania będzie to standardowo obsłużone.

 

Zapytania i szyna zapytań

Interfejs bazowy dla zapytań, musi zawierać typ generyczny jaki zostanie zwrócony.

IQuery baza dla wszystkich zapytań

TResult – typ generyczny, określający zwracany wynik wykonania zapytania.

 

IQueryHandler – podobnie jak poprzednio (ICommandHandler), implementując ten kontrakt tworzymy kod przetwarzający logikę, a następnie zwracany jako typ generyczny TResult.

 

IQueryBus, kontrakt dla szyny danych, analogicznie jak dla komend z tą różnicą, iż trzeba uwzględnić typ generyczny TResult.

 

QueryBus to implementacja interfejsu IQueryBus.

 

Proces przetwarzania komendy można przedstawić w następujący sposób:

 

  1. Przekazanie zapytania do szyny przy pomocy wywołania metody Process.
  2. Jeżeli przekazane zapytanie jest pusta, to rzuca wyjątkiem ArgumentNullException,
  3. Wyciągnięcie z kontenera (Autofac) handlera dla przekazanego zapytania.
  4. Jeżeli handler jest pusty to rzuca wyjątek Exception.
  5. Tworzenie typu zwracanego z domyślnymi wartościami.
  6. Wywołanie zapytania poprzez metodę Execute w bloku try…catch.
  7. Jeżeli wywołanie się nie powiedzie to zostanie zapisany log błędu.
  8. Zwracanie wyniku przetwarzania zapytania.

 

 

 

Pierwsze zapytanie

Poniżej znajduje się pierwsza implementacja zapytania GetApplicationInformation, jako typ generyczny wykorzystana jest klasa ApplicationInformation, i to właśnie taki obiekt zostanie zwrócony po wykonaniu zapytania.

 

Za logikę wykonania zapytania odpowiedzialny jest handler GetApplicationInformationHandler. Jego celem jest pobranie z Assembly aktualnej wersji programu, a następnie zwrócenie jako klasę ApplicationInformation wyniku wykonania zapytania w metodzie Execute.

 

Model jaki jest zwracany po wykonaniu zapytania GetApplicationInformation.

Użycie zapytania pobierania informacji aplikacji wygląda następująco:

var applicationInformation = QueryBus.Process<GetApplicationInformation, ApplicationInformation>(new GetApplicationInformation());

 

Testy

Automatyczne tworzenie obiektów przez Autofac przyczynia się do potrzeby nadpisania handlera dla testowanego zapytania.

Tak jak w przypadku komend, należy przygotować mocka dla interfejsu IQueryHandler.

Dodatkowo określamy co ma zwracać metoda Execute, a dokładniej, z jakiego delegata ma skorzystać po jej wywołaniu.

Dlatego każdy test korzystający z klasy QueryBaseTests, musi zaimplementować delegata. To pozwoli na dowolną imitację działania logiki zapytań.

Po przygotowaniu obiektu fake dla handlera, ponownie rejestrujemy w kontenerze.

Po tej operacji oryginalna metoda Execute z handlera jest już nadpisana, i możemy śmiało testować logikę wykonywanych zapytań.

 

 

Implementacja pierwszego testu, do generowania losowych ciągów znaków została wykorzystana biblioteka AutoFixture, spowoduje to losowość w trakcie uruchamiania testu.

Do prawidłowego działania testu należy ustawić delegat handleMethod (podobnie jak w przypadku testowania komend). W tym przypadku wykorzystujemy lambdę, i ustawiamy klasę jaka zostanie zwrócona przy wywołaniu metody Process z handlera dla szyny zapytań.

Po wykonaniu zapytania można sprawdzić wynik przy pomocy asercji: applicationInformation.Version.ShouldBe(version);

 

Na koniec

Po implementacji komend i zapytań, mamy do dyspozycji bardzo potężny mechanizm, który można wykorzystać jako filar do budowy wielu aplikacji.

Wcześniej nigdy nie korzystałem z tego podejścia, nie myślałem nawet o takiej formie.

Moja interpretacja i implementacja zapewne odbiega od ideału.

 

Kolejnym krokiem będzie wykorzystanie Event Sourcing, i być może dodanie walidatorów, jednak to jest już temat na kolejny post.

Rozważam, możliwość wyodrębnienie a projektu PictOgr, implementacji CQRS, tak by w przyszłości można było wielokrotnie wykorzystywać efekty mojej pracy.

 

 

 

Dziękuję za wytrwałość i zachęcam do komentowania.

 

Jest to post przygotowany na potrzeby konkursu „Daj Się Poznać 2017” organizowanym przez Macieja Aniserowicza.