Marcin Mroczkowski Programista JAVA/JEE
Temat: CQRS czyli co....
Po ponad pół roku edukacji w kierunku DDD, połknięciu książki Erica Evansa i po przeczytaniu niezliczonej ilości opracowań, łącznie z serią z bottega.com(przytoczoną przez Łukasza) muszę stwierdzić, że CQRS ma sens i to zaskakująco duży. Popełniłem podobny projekt na którym się uczę i efekty są bardzo zachęcające.Przede wszystkim DDD koncentruje się na optymalizacji i ekspresji głównego modelu dziedziny. Podejście te ignoruje całkowicie zagadnienia prezentacji i persystencji, co przy nieodpowiedniej realizacji technologicznej może skończyć się tragedią wydajnościową. Jeśli korzystamy z grubych agregatów do tworzenia widoku to zaatakuje nas wiele problemów:
- sztandarowe N+1 select problem
- jeśli zaczniemy optymalizować agregaty pod kątem wydajności bazodanowej, to zaprzeczymy całej idei DDD i zmarnujemy cała pracę polegającą na czystym wyrażeniu domeny
- lazy loading wcale nie jest złotym rozwiązaniem tych problemów, owszem może pomóc, ale tak niesie z sobą narzut wydajnościowy, ale co najgorsze... przeciąga zakres transakcji, ponieważ wszystkie lazy pola muszą być zaciągnięte w managed encjach
- naruszamy enkapsulację agregatów bo wyciągamy z nich dane getterami
CQRS rozwiązuje ten bolączki bazując na spostrzeżeniu, że agregaty nie są do niczego potrzebne przy tworzeniu widoków. Jest to niejako obejście modelu dziedzinowego, który odpowiada za operacje biznesowe.
Nie mogę zgodzić się z wieloma twierdzeniami, które tutaj przeczytałem, w szczególności:
1.) potrzebny jest kolejny model i przez to dublujemy tą samą logikę
Rzeczywiście, pracy jest trochę więcej, bo musimy zaimplementować odpowiednie DAO, które wyciągnie dane z bazy, ale już nie przy użyciu agregatów. DTO dla widoku i tak musi powstać, więc możemy je użyć jako nośnik danych używany przez query interface(CQRS wcale nie narzuca konkretnego modelu query, to już zależy od potrzeb). Podsumowując roboty dodatkowej nie jest aż tak dużo jak się wydaje i jest to proste "klepanie", które dziecinnie łatwo przetestować i implementować. NIE jest to to samo co implementacja domeny, to są dwie całkowicie różne rzeczy.
2.) zmiana db pociągnie lawinę zmian w query modelu
Nie pociągnie... od tego jest abstrakcja uzyskiwana przez obiekty DTO widoku, żeby sposób ich uzyskiwania był przykryty z perspektywy GUI. Jeśli zmieni się silnik danych, czy zrefactorujemy agregat, to zmieni się jedynie implementacja dao wciągającego te dane, z tym muszę się zgodzić. Jeśli chodzi o poważniejszą zmianę modelu (np. dodane zupełnie nowych danych), to już tutaj żadna abstrakcja nie pomoże i będzie potrzebna zmiana we wszystkim od domeny, przez bazę po widok.
3.) wymusza wyciąganie logiki biznesowej do widoku
Moim zdaniem całkowicie błędny argument, wynikający z nieprawidłowej klasyfikacji niektórych zagadnień aplikacji. Proszę o dokładnie wytłumaczony i konkretny przykład takiej sytuacji, ponieważ jakoś nie potrafię sobie wyobrazić, żeby aspekt widokowo/raportowy nagle potrzebował wykonywać funkcję z core domain.
4.) nie ma różnicy ile interfejsów zastosujemy, ważna jest ilość maszyn
Nie jest to prawdą, w standardowych systemach biznesowych ponad 95% kontaktu z bazą danych to zapytania, nie modyfikację. Maksymalna optymalizacja zapytań ma sztandarowe znaczenie dla wydajności systemu i CQRS pozwala nam w bardzo przejrzysty sposób oddzielić oba aspekty. Daje to ogromne pole do popisu dla specjalistów od baz danych, którzy mogą zoptymalizować bazę danych pod zapytania, używając np. eventual consistency. Inaczej mówiąc CQRS pozwala wprowadzić aplikację pisaną pod programistów (DDD), w świat największej wydajności, w której nawet najbardziej hardcorowe techniki optymalizacyjne są możliwe.
CQRS w gruncie rzeczy daje nam możliwości architekturalne, nie zabierając na początku niczego. Nikt nam nie zabrania w pierwszej wersji aplikacji użyć grubych agregatów do implementacji query interface... Sposób tworzenia DTO jest zakryty, a widoku nie obchodzi skąd dostaje dane. W ten sposób masz namiastkę CQRS, bez jakichkolwiek konsekwencji. Jedna różnica, to posegregowane interfejsy. Jeśli chcesz większą wydajność to używasz HQL lub SQL. Im niższej implementacji użyjemy tym bardziej wiążemy naszą implementację query interface z bazą, ale też zwiększamy wydajność. To kiedy w tym procesie się zatrzymamy zależy tylko wyłącznie od nas i to jest chyba w tym wzorcu najlepsze.