Przejdź do treści

DevStyle - Strona Główna
Jak zwrócić rezultat wykonania komendy w CQRS?

Jak zwrócić rezultat wykonania komendy w CQRS?

Maciej Aniserowicz

29 listopada 2016

Backend

W tekście “CQRS+DI w C# i Autofac” pokazałem, że CommandHandler nie zwraca żadnego rezultatu wykonania komendy. Natomiast w “Esencja CQRS” pisałem, że jest to jedna z zasad, co do której można się spierać. Więc… jak to faktycznie jest?

[blogvember2016 no=”23″]

Wrzucanie komendy w system i “pójście dalej” bez żadnej informacji o rezultacie bywa możliwe, lecz zwykle ciężko byłoby to zaimplementować. No bo jak to: kazałem coś zrobić mojej aplikacji i nie wiem czy się udało, więc… co dalej? Gdzie pokierować użytkownika?

Moje systemy są wyjątkowe!

I to w ilu znaczeniach! Nawet nie chodzi o to, że są super. Albo, że nigdzie na świecie nikt nie wymyślił takich. Bo przecież jest dokładnie odwrotnie.

Chodzi o to, że moje komendy – jak zresztą KAŻDY kawałek kodu – MOŻE zwracać rezultat. Nawet, jeśli sygnatura mówi: void. Bo niekoniecznie musi to być int, bool, enum czy pełnoprawna klasa. W ogromnej – naprawdę OGROMNEJ – większości przypadków nie potrzebuję konkretnych danych będących wynikiem wykonania operacji. Potrzebuję tylko jednej informacji: udało się, czy nie?

A jak można taką informację osiągnąć z kontraktu zwracającego void? Hmm, no zastanówmy się… Czyż nie będzie logiczne takie założenie: “nie wywaliło się, czyli się powiodło”? Tak, będzie! Rozwiązaniem są wyjątki.

O sposobie implementacji oraz motywacjach kryjących się za tym podejście rozpisałem się już kiedyś w tekście “Custom Exceptions“, więc nie będę się tutaj powtarzał. Odsyłam na chwilę do tamtego posta. Idź, idź. Jak Mietek Fogg – to był gość – “ja mam czas, ja poczekam“.

Już? W ilu głowach w tym momencie pojawiło się takie wykrzyknienie:

Przecież nie można “sterować programem za pomocą wyjątków“!

Zgadza się, jak najbardziej. Ale kiedy w takim razie MOŻNA zastosować wyjątek? Czyż sama nazwa nie wskazuje na to, że… w sytuacji wyjątkowej?

Pozostaje kwestia definicji takiej sytuacji. Najpierw to ustalmy, a najwyżej potem będziemy się kłócić. Przedwczesna kłótnia jest gorsza niż “prematuralne” wiadomo co.

W moim pięknym świecie – a przypomnę ponownie, że teorię tę weryfikowałem przez lata stosowania jej w praktyce – obowiązuje takie coś:

Sytuacja wyjątkowa (rzecz.) – każda ścieżka różna od legendarnej “happy path”

Użytkownik chce zarezerwować na koncert więcej biletów, niż jest dostępnych w systemie? Wyjątek – jakaś walidacja powinna to wcześniej wyłapać! Ktoś chce zadzwonić do klienta, ale w numerze telefonu mamy literki zamiast cyferek? Wyjątek – niepoprawne dane w bazie! Przechodzimy z koszyka do kasy, a w międzyczasie sesja wygasła? Wyjątek!

Można by takie scenariusze mnożyć. To NIE JEST sterowanie wyjątkami. To jest zasygnalizowanie sytuacji wyjątkowej. Z tą różnicą, że ja – jako programista – przewidziałem możliwość jej wystąpienia. Co wcale nie czyni jej… mniej wyjątkową!

Wyjątek od reguły

Jest jedna – słownie: jedna – sytuacja, w której naprawdę przydałoby się zwrócenie z komendy konkretnej wartości. Jest to scenariusz, w którym użytkownik dodaje do systemu nowy obiekt i od razu chcemy przekierować go do ekranu szczegółów/edycji tegoż. Żeby to zrobić: musimy znać ID obiektu.

Czy dla jednego scenariusza warto komplikować całą infrastrukturę, dodając wsparcie dla zwracania wartości przez komendy? Moim zdaniem: zdecydowanie nie.

Więc jak sobie poradzić? Poniżej dwa przykładowe rozwiązania. Jest ich pewnie więcej.

Sposób 1 (skomplikowany). Wysyłamy do systemu komendę “utwórz obiekt”. System ją przetwarza, obiekt zostaje utworzony. Następnie system komunikuje się w drugą stronę, aktywnie wysyłając wiadomość do klienta: powstał nowy obiekt z {id}. Klient przekierowuje użytkownika. Wymaga to sporo zachodu, bo na kliencie musimy zaimplementować kanał przyjmowania wiadomości od serwera.

Sposób 2 (prostszy). Musimy tylko ZNAĆ ID obiektu PRZED jego utworzeniem, rajt? Da się to zrealizować nawet bez rezygnowania z “autoincremented primary key identity integer column” w bazie, która dla wielu jest świętością. Wystarczy dodać… kolejną kolumnę! UniqueId, typu GUID. I generować to ID po stronie klienta, przesyłając je wraz z pozostałymi danymi potrzebnymi do utworzenia obiektu. Po zakończeniu przetwarzania komendy (czyli: jeśli nic się nie wywaliło) możemy spokojnie założyć, że obiekt o takim ID znajduje się w systemie i jest właśnie tym nowym, o który nam chodzi.

Zrobione? Zrobione! A system nadal jest prosty? Nadal.

Jak zakończyłby swój wywód matematyk-taksówkarz: “co należało dowieść“.

Fajrant.

Zobacz również