DDD? A po co to komu?


Kiedy pojawia się tego typu "nowa" terminologia jako branża zawsze zastanawiamy się nad tym co ten nowy buzzword znowu chce nam powiedzieć i gdzie można tego użyć. Oczywiście są dość powszechne próby podejść naiwnie czerpiące wszystkie technologiczne rozwiązania nawet bez rozpoznania "terenu" i tego jaką korzyść w konkretnej sytuacji dają nam konkretne rozwiązania. Wtedy pojawia się trend spadku popularności i nazywanie koncepcji "buzzwordem", próby powrotu do starych "dobrych" praktyk. Każda taka ideologia sprawdza się jednak w określonych sytuacjach i pomimo, że proponuje komplementarne rozwiązania niczym filozofia XIX wieku próbująca opisać cały świat w jednym spójnym ujęciu ideologicznym. Filozofia XIX wieku doprowadziła do totalitaryzmu i dziś tego typu architektoniczne Nazi jednostki są dość powszechne. Jednak żyjemy w XXI wieku i teraz wiemy już to czego nauczył nas wiek XX czyli wiemy, że ideologie sprawdzają się w określonych kontekstach i tam są one dobre głównie do potrzeb archiwistycznych, porządkujących w tymczasowym paradygmacie informacje które są bogatsze niż to do czego potrzebuje ich domena. Myśląc jednak o przeznaczeniu, o celu jaki chcemy osiągać i kontekście domenowym redukujemy złożoność do modularnych domen.

Kluczowe koncepcje DDD i połączone z tym pojęcia:

- wspólny język (zawsze stanowiło to dla mnie problem w zastanych systemach kiedy spotykałem projekty w których wszędobylskie było słowotwórstwo i synonimy tworzone po to by oddzielić detale implementacyjne i np. obiekty obiekty transmisyjne od encji za pomocą innych słów, a biznes operował w tym czasie zupełnie innymi pojęciami). 

- eksperci domenowi (jedynie pracując w firmach posiadających własny in-house team programistów pracowało się dobrze, bo tam programista był człowiekiem rozwiązującym problemy innych, a nie źródłem problemów z czasem wymaganym na definiowanie wymagań) definiujący funkcjonalności za pomocą zdarzeń systemowych (zdarzenia wyeliminowały potrzebę określania funkcjonalności za pomocą nieprecyzyjnego opisu wymagającego wiedzy z jednej dziedziny od eksperta z innej kiedy proces dotyka kilku domen).

- konteksty powiązań i agregaty (o relacyjnych bazach danych, bałaganie i problemach jakie się z tym łączą dobrze opowiada drugi z załączonych filmów) łączące encje i obiektowe właściwości w jeden kontekst transakcji, która będzie zakończona w izolacji od zachowania innych części systemu a w przypadku kiedy inna domena zgłasza wyjątek będzie kompensowana osobnym zdarzeniem odwracającym skutki.

CQRS i event sourcing (zdarzenia nie transmitują stanu ale są poleceniami zmiany i nie operują na pełnym kontekście stanu obiektu, co umożliwia kompensowanie zdarzenia bez zamrażania stanu. Słowem "dodaj jeden" jest odwracalne, a "ustaw wartość na dwa" nie jest odwracalne. Ciąg zdarzeń doprowadza do odtworzenia stanu obiektu niczym w systemie opartym na blockchain, a stan jest zachowywany jedynie jako "read model" i nie wykonuje się na nim innych operacji niż zwrócenie stanu do innego systemu, np. UI)

Chciałbym byście zwrócili uwagę na główne cechy DDD i to w jaki sposób możemy je zaaplikować osobno, bez wchodzenia w cały bardzo złożony model który wyłania się z czerpania pełni zakresu jaki ujmuje ta koncepcja, oraz byśmy zwrócili uwagę na to jak różne jest ujęcie domenowe od ujęcia heksagonalnego w kontekście budowania wyseparowanych modułów odzwierciedlających architekturę warstwową. Także na to ile z ujęcia technologicznego rzeczywiście jest w stanie wpłynąć na domenę, a na ile ujęcie technologicznego podziału domeny realizowane jest już przez frameworki jest wyabstrahowane z kodu który tworzymy.





Technologicznie zorientowana potrzeba separacji jest nie tylko istotna dla nas jako dla programistów, budowniczych systemu, ale jest ona wpisana w podejście realizowane przez frameworki. Framework bierze na siebie implementację tego co jest związane z warstwą której już nie musimy pisać. Zostajemy z kilkoma "smrodkami" w postaci adnotacji ale zyskujemy możliwość oznaczenia naszego agregatu jako encji bazodanowej i załączenia innych komponentów DDD, spięcia z nim i zapisywania podczas jednej transakcyjnej w pełni operacji. Możemy zmienić kilka adnotacji i zapisać to np. w dokumentowej bazie danych. Taka zmiana w obszarze jednego agregatu jest zazwyczaj dość trywialna. Jak więc połączyć myślenie kategoriami bazy danych relacyjnej, z agregatami w kontekście aplikacji pianych w Springu? Odpowiedzi jest wiele ale praktycznie widzę problem z modelowaną relacyjne bazą w kontekście ORM. Najczęściej mamy piękny podział na agregaty i DDD w kodzie ale operujemy na bazie która została wymodelowana tak, że każdy moduł funkcjonalny i każdy agregat potrzebuje innych. Pytaniem zasadniczym uczynił bym pytanie o to czy potrzebujemy Hibernate. 


Podejściem które często praktykowałem i które uważa za sensowne jest rozbijanie bazy danych na obszary niepołączone ze sobą za pomocą kluczy obcych i zapisywanie agregatów dokładnie tak jak zapisuje się grafy, a więc każde zagnieżdżenie to osobna tabela i tabele takie nie są współdzielone między agregatami, ale są ich wewnętrznymi własnościami. Wtedy nie ma znaczenia czy używamy ORM czy np. Spring Data z JDBC. Hibernate daje nam dodatkowe elementy takie jak optymistyczne blokowanie na poziomie bazy danych, chociaż wymaga trochę więcej konfiguracji, lecz jeśli każdy moduł utrzymuje własne struktury tabel w izolacji, posiada własne encje. Bardzo dobrym sposobem na podzielenie bazy danych w ten sposób może być osobna schema dla każdego agregatu i wprowadzenie umownego zakazu ich łączenia w obrębie jednego zapytania sql.  

Często spotykanym problemem jest np n+1 w takim rozproszonym systemie i należy pamiętać by dociągać elementy z innego agregatu za pomocą metod typu getAllById(Collection<UUID> idCollection)

Wracając do mojego tekstu o typach architektury powiedziałbym, że mieszanie wszystkich elementów koniecznych do zaimplementowania biznesu w kontekście DDD i wydzielanie warstw technologicznych tworzy najgorszy z wariantów korporacyjnej architektury typu wiadro, o bardzo dużym poziomie złożoności zabijającym całkowicie wydajność pracy w zespołach. Ponadto wiele razy w takich projektach widziałem ten sam błąd jakim jest przydzielanie pracy nad każdą warstwą innej osobie w formie zadania, przy jednoczesnym braku dokładnego określenia kontraktu (kiedy zespół miał tworzyć kontrakt na spotkaniach trwały one w nieskończoność). Słowem skończmy to szaleństwo i jeśli nie piszemy bibliotek ogólnych zastosowań lub sterowników do urządzeń a zajmujemy się realizacją konkretnego przypadku biznesowego na warstwie bazy danych. Zamiast tego korzystajmy z rozwiązań takich jak Spring Data. Nie wszystko musimy przykrywać własnymi klasami fasadowymi.

Comments