Od kilku lat w firmie piszemy kod zgodny z podejściem CQRS. Uzyskaliśmy w ten sposób rozdzielenie zapisu od odczytu – bardziej namacalne niż dwie różne metody ;) Daje nam to wiele możliwości skalowania, prostsze podejście do kodu i pewne możliwości optymalizacji strony serwerowej. Dla przykładu w ogólnie nie ma problemu z innym źródłem danych do odczytu a innym do zapisu, czy też wieloma źródłami danych do odczytu i jednym to zapisu – na przykład z denormalizowana postać bazy danych do odczytu, i z normalizowana do zapisu.

Taki podział, spowodował, że nasz kod jest naprwadę prosty. Banalny. Polegający na tym, żeby wywołać odpowiednią komendę lub odpowiedni read model. Gdzie komendy są proste jak konstrukcja cepa z paroma “jeśli”. Ogólnie nic co wymaga zastanowienia się, wystarczy przeczytać kod i wszystko będzie jasne. ReadModel zaś po tym jak już udało mi się wymusić tworzenie widoków, stał się też bardzo prosty. Momentami polega on na zrobieniu prostego zapytania i z mapowania wyniku na lokalny ViewModel. ViewModel różni się tym od widoku, że zawiera pewne własności które są niezbędne do działania widoku UI a zarazem korzystają z danych już posiadanych (dane z bazy lub te które mamy dostępne pod ręką jak nazwa użytkownika).

Kod stał się tak prosty że programiście stwierdzili, że po co go duplikować. Zaczęli re-wykorzystywać widoki i ReadModele – w końcu po co mieć coś dosłownie takiego samego dla kolejnego widoku… Pół biedy, zaczęli wykorzystywać ViewModele na zasadzie tworzenia zmiennej w nowym ViewModel odwołującej się do typu z innego widoku UI.

Zaczęła się robić MIAZGA. Niby wszystko działała i pewnie by działało, gdyby nie zmiana dotycząca dwóch widoków  – trzeba było usunąć jakieś pole, dodać inne oraz usunąć widok bo już nie był potrzebny.

Prosta zmiana? Oszacowana na 1h z unit testami, plus 1h testów regresyjnych. Po tygodniu pracy wciąż nie byliśmy wstanie dostarczyć aplikacji. A wszystko przez to, że jak zobaczyłem tą siatkę powiązań aż nie mogłem wytrzymać i trzeba była ją rozplątać. Dla przykładu pole które zostało usunięte z widoku, było wykorzystywane w 5 innych miejscach i to nie tak wykorzystywane by było, było one odpowiedzialne za pewne zachowania widoków więc nie mogło go zabraknąć. Usunięty widok zaś był na szczęście wołany tylko w 4 miejscach z czego tylko 2 z niego korzystały…

W tym wypadku zmiany były proste, ale wywołały one wilka z lasu i przypomniały o dużo większych problemach jakie mieliśmy. W jednym z projektów jeden dev zamiast zwracać stronicowane dane z bazy zawracała całą zawartość bazy i re-wykorzystywała to w kiku miejscach. Zmiana polegała na zrobieniu stronicowania…

Rozwiązaniem problemu jest zrozumienie, że taki sam read model co do linijki w kodzie z inną nazwą widoku (co do joty z takim samym SQL jak inna), nie jest duplikacją kodu. Jest implementacją wymagań danego widoku, który aktualnie ma być taki, zaś może on ulec w każdej chwili zmianie. Nikt nie mówi ze widok w bazie danych nie może bazować na jakimś innym. Może, tylko zawsze pamiętajcie, że by ten bazowy był bazowy a nie widokiem do innego wymagania.

Nawet jak nie korzystanie z CQRS, ale zwracacie dane do widoku w MVC czy gdziekolwiek indziej, pamiętajcie, ten model który zwracacie jest unikatowy dla tego widoku. Koniec KROPKA jakakolwiek dalsze rozpisywanie się tutaj nie ma sensu. Może być tylko jedne View, ViewModel, ReadModel per widok ui.

10 KOMENTARZE

  1. LOL! Koniec KROPKA jakakolwiek dalsze rozpisywanie się tutaj nie ma sensu

    Przypomniał mi się projekt gdzie miałem dodać jedną w sumie małą funkcjonalnością. Roboty na 2 dni bo projektu nie znałem więc pierwszy dzień to rozkminianie OCB. No i… okazało się, że wszystkie kontrolery (MVC) dziedziczyły z jednego w którym była cała logika dostępu do bazy no i wszystkie modele dziedziczyły między sobą bo po co propertisy mnożyć, odziedziczę.

    • @brogowski

      ;) tak i nie. Jeżeli w danej chwili są takie same i zakłada się, że ulegną z tego samego powodu zmianie. To nie oznacza, że po zmianie będą takie same. Nie ma wyjątków :)

    • @Bartosz Sosna

      Pod każdym względem :) composition over inheritance. Oczywiście są przypadki kiedy dziedziczenie ma ręce i nogi. Ale naprawdę to są… przypadki.

      Ostatni raz użyłem dziedziczenia przy wiadomościach MSMQ dla MassTransit. Po pół roku wywaliłem je, bo powodowało więcej komplikacji niż korzyści – ograniczało, zmuszało do albo rozszerzenia wszystkiego o albo przystosowanie jednego do ogółu. Żadna opcja nie pasowała.Takich przypadków/przykładów jest dużo :)

Comments are closed.