W ostatnim wpisie poruszyłem temat CQRS z perspektywy laika. Napisałem, że chciałbym zaimplementować mechanizm znaleziony na stronie devstyle w swoim kodzie. Byłem przez to zmuszony do użycia po raz pierwszy refleksji i muszę Wam powiedzieć… ME LIKEY!
Utworzenie znacznikowych interfejsów
Maciej Aniserowicz swoje rozwiązanie przedstawił w C#, natomiast ja stanąłem przed wyzwaniem, aby je dostosować do realiów Javy. Rozpocząłem, więc od utworzenia kilku interfejsów. Zacznijmy od przyjrzenia się przypadkowi komend obsługujących zapis w CQRS.
Interfejs Command nie jest niczym szczególnym, ponieważ nic w sobie nie ma. Jest to tylko znacznik dla refleksji. Klasy reprezentujące komendy muszą go po prostu zaimplementować. Natomiast CommandHandler posiada już w sobie metodę handle (wskazuję jeszcze raz, że zwraca void), która będzie obsługiwać TYLKO JEDNĄ wybraną komendę. Jest to bardzo ważne, ponieważ wynika z założeń architektury. Na koniec zostaje szyna CommandsBus obsługująca cały ten proces. Jej implementacja powinna zawierać sposób dopasowywania komend do ich handlerów. I w tym momencie przychodzi nam na pomoc właśnie… REFLEKSJA!
Kierownik, czyli szyna CommandsBus
Będziemy poszukiwać par CommandHandler - Command i rejestrować je w mapie Map<Type, CommandHalder> handlers, gdzie Type reprezentuje superinterfejs dla wszystkich typów w Javie. Poniższy kawałek kodu wyłuskuje dla wybranego handlera komendę jaką ten ma się opiekować.
Jeżeli dany handler nie będzie poprawnie zaimplementowany dostaniemy wyjątek przy starcie aplikacji. W konstruktorze szyny przekazujemy zbiór naszych handlerów, które można wstrzyknąć np. przy pomocy Springa.
Wygląda to praktycznie identycznie jak w przypadku komend. Jedyną różnicą jest to, że w szynie mamy metodę publish a nie send. Implementacja poszukiwania czy handler poprawnie obsługuje event jest również bliźniaczo podobna do wcześniej przedstawionej.
Należy zwrócić uwagę na fakt, że używamy tutaj Collectors.groupingBy, ponieważ jeden event może mieć WIELE handlerów. Wynik przypisujemy do mapy Map<Type, Set<EventHandler>> handlers. Całość znowu możemy wykorzystać w frameworku wykorzystującym wstrzykiwanie zależności.
Teraz tylko wstrzykujemy do wybranej klasy EventsBus i wywołujemy na nim eventsBus.publish(event).
Podsumowanie
Przygoda z implementacją CQRS rozpoczyna się naprawdę ciekawie. Nie wiem jak to będzie wyglądało w praktyce w mojej aplikacji AnimalShelter, ale jestem dobrej myśli po przeklinaniu tego w testowym projekcie. Kod można znaleźć w tym miejscu. W następnym artykule przedstawię testy jednostkowe jakie udało mi się napisać do tego rozwiązania.