PictOgr – mój CQRS -1-

Command Query Responsibility Segregation – 1 –

PictOgr - mój CQRSZachciało mi się… nauczyć czegoś przydatnego i noweg. Padło na separację operacji pobierania i zmieniania danych. W tym celu pokusiłem się o własną implementację CQRS. Wszystkie komponenty składowe ładowane przez Autofaca w odseparowanym module.

Funkcjonalność w połączeniu z MVVM funkcjonuje dobrze, a i ja się czegoś nowego nauczyłem.

Tym samym dzielę się ze światem, informacjami na tema rozwoju PictOgr-a.

Zważywszy, iż mam duży bagaż doświadczeń (nie koniecznie dobrych), i zmiana podejścia do budowania aplikacji z wykluczeniem code-behind jest dla mnie bardzo ekscytująca. W końcu od ponad 20 lat uwielbiam się rozwijać w IT, a to kolejna okazja.

 

Struktura w projekcie

Struktura mojego CQRSa w projekcie wygląda tak jak na przedstawionym obrazku.

Rozdzieliłem bazę do budowania komend i zapytań w podfolderze CQRS wraz z odpowiednimi nazwami:

  • Bus – implementqacja szynu dla komend i zapytań zawierająca interfejs szyny komend (ICommandBus) i zapytań(IQueryBus) w raz z ich implementacją (CommandBus, QueryBus),
  • Command – kontrakty komend w skład których wchodzi interfejs: ICommand i ICommandHandler,
  • Query – kontrakty dla zapytań i tu wyodrębnić można: IQuery, IQueryHandler.

 

Wszystkie komponenty bazowe CQRS-a tworzone są w module CQRSModule.

 

Komendy i szyna komend

 Pora nieco omówić implementację komend, do budowy zostały wykorzystane następujace kontrakty:

  • ICommand – bazowy interfejs dla każdej komendy nie posiada żadnego szkieletu, poprzez implementację określamy dane jakie należy przezkazać do handler-a  komendy,

 

  • ICommandHandler – interfejs jaki należy zaimplementować w klasie handler-a, tego typu komenda nie otrzymuje żadnych danych,
  • ICommandHandler<ICommand> – tworząc implementację na bazie tego kontraktu, przekazać należy dane, które to następnie zostaną przetworzone przez handlera.

 

ICommandBus to kontrakt dotyczący szyny komend, na którą będą wrzucane komendy i przetwarzane. 

 

Implementacja szyny komend CommandBus.

 

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

  1. Przekazanie komendy do szyny przy pomocy wywołania metody SendCommand.
  2. Jeżeli przekazana komenda jest pusta, to rzuca wyjątkiem ArgumentNullException,
  3. Wyciągnięcie z kontenera (Autofac) handlera dla przekazanej komendy.
  4. Jeżeli handler jest pusty to rzuca wyjątek Exception.
  5. Wywołanie komendy poprzez metodę Handle w bloku try…catch.
  6. Jeżeli wywołanie się nie powiedzie to zostanie zapisany log błędu.

 

Pierwsza komenda

Pierwsza komenda jaka została zaimplementowana ExitApplication dotyczy zamykanai aplikacji.

Dane jakie zostają przekazane to kod wyjścia.

 

Kod błędu wykorzystany jest przez handlera ExitApplicationHandler, jest on wymagany przez metodę System.Environment.Exit(ExitCode).

Dodatkowo zapisywany jest log informujący. iż aplikacja została zamknięta.

 

Kod poniżej przedstawia, użycie komendy zamykania aplikacji, w komendach wywoływanych przez widok MVVM.

Do obiektu ExitApplicationCommand została wstrzyknięta szyna komend.

Uzycie komendy ExitApplication, sprowadza sie do 1 linijki kodu commandBus.SendCommand<ExitApplication>(new ExitApplication(0));

 

Testy

Do przetestowania działania komend w ujęciu testów jednostkowych wymagane jest przysłonięcie metody Handle.

W tym celu napisałem specjalną klasę matkę z mockiem (zaznaczone linie 18-27 w kodzie).

Ponieważ w projekcie został zastosowany mechanizm automatycznego rejstrowania obiektów w Autofacku, należy podmienić (ponownie zarejestrować) komendę jaką chcemy przetestować.

Dodatkowo stosuję tutaj typ generyczny, tak by użyc klasy bazowej dla większej ilości testów.

Metoda handleMethod jest to delegat jaki musi być przypisany w teście.

 

Powyższa implementacja jest rozszeżana we właściwym kodzie testu ExitApplicationCommandTest.

Przesłnięcie metody handleMethod, pozwala na pobranie kodu wyjścia z wywoływanej komendy.

Kod następnie jest przepisany do zmiennej lokalnej, i wówczas możemy już wykonać asercję: exitCode.ShouldBe(expectedValue);

Drugi test jaki został zaimplementowany to sprawdzenie działania komendy wywołanej z widoku w celu zamknięcia aplikacji.

W tym przypadku dane jakie zostają przekazane do komendy ustalane są w klasie ExitApplicationCommand, i kod prawidłowego wyjścia jest równy 0.

Dlatego asercję należy wykonać do wartości 0.

 

Koniec

To tyle jeśli chodzi o zaimplementowany przeze mnie fragment CQRS dotyczący komend, w następnej części przedstawię implementację zapytań.

Zastanawiam się także nad wykorzystaniem ES w projekcie, oraz reorganizacją projektu w celu wyodrębnienie CQRSa do osobnej biblioteki.

 

 

Jeżeli ktoś wytrzymał do końca to dziękuję za uwagę i zapraszam do dalszego śledzenia bloga.

 

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