sobota, 1 listopada 2008

UP-DDD in Action: Malutka Teoria Unifikacji

Planowałem stopniowo wprowadzać w fazy Unified Process, jednak jak widzę zniecierpliwieni czytelnicy już teraz zastanawiają się jak pogodzić ze sobą UP i DDD:) Maciek - specjalnie dla Ciebie zwiększamy tempo.

W skrócie UP zakłada etapowe przeprowadzenie projektu. Etapy mogą (powinny) być powtarzane iteracyjnie. Z kolei etap możemy podzielić na fazy. Fazy są właściwie spojrzeniem z rożnych poziomów abstrakcji na nasz system. W każdej fazie powstają na wyjściu pewne artefakty, które są wejściem do fazy następnej. Po ostatniej fazie zamykamy iterację i powinniśmy mieć działający system.

W dużym skrócie i uproszczeniu mamy fazy:
- analizy biznesowej
- analizy systemowej
- projektowania (opartego na założonej/wypracowanej architekturze)
- implementacji
Kiedyś popełniłem ich zgrubny opis i niech to nam na razie wystarczy (druga połowa posta). Póki co warto pamiętać, że w fazie analizy powstaje model dziedziny problemu a w fazie projektowania powstaje między innymi model obiektów biznesowych. Poczucie estetyki i intuicja biją na alarm: dwa modele to co najmniej o jeden za dużo! Gdzie jest Ockham ze swoją brzytwą?!

Domain Driven Design skupia się na fazie analizy systemowej i projektowania. Ojciec założyciel DDD (Eric Evans) dokonał niebywałego odkrycia;) Unifikacja! Co prawda nie jest ono na miarę GUT ale wnosi coś nowego do UP. Mianowicie utrzymywanie dwóch modeli: analitycznego i projektowego prowadzi do zasadniczego problemu: modele z czasem się rozjeżdżają. Cholera - nikt się chyba tego nie spodziewał (tak jak hiszpańskiej inkwizycji). Z czasem modyfikacje wprowadzane na poziomie projektu nie są aktualizowane na poziomie analizy. A z drugiej strony nowe odkrycia analitycznie nie są projektowane z braku czasu.

Zatem nie udawajmy, że wszystko jest git i nie oszukujmy się, że grzecznie będziemy pilnować spójności obu modeli. W brutalnej rzeczywistości komercyjnych projektów korporacyjnych to se ne da i kropka. Dobrze wiemy, że wystarczającym problemem jest już samo utrzymanie spójności pomiędzy implementacją a modelem projektowym:)

Zamiast walczyć z jakimś zjawiskiem, nauczmy się z nim żyć, albo jeszcze lepiej wykorzystajmy na własną korzyść. To właśnie postuluje DDD. Niech nasz projekt obiektów biznesowych będzie jednocześnie analitycznym modelem domenowym. Przecież sama koncepcja obiektowości powstała po to, aby struktury techniczne odpowiadały mniej więcej bytom z dziedziny problemu. Oczywiście analityk czasem musi się nagiąć nieco w stronę technikaliów i uszczknąć nieco ze swego pięknego modelu absolutu. Z drugie strony projektant musi zapomnieć o optymalnym modelu klas czy bazy. Na szczęście doświadczony projektant OO jest w stenie w miarę wiernie oddać strukturę domeny biznesowej. W razie czego kompromis to podstawa. Heh szczęście w nieszczęściu gdy analitykiem i projektantem jest jedna i ta sama osoba:)))



Opowiadając już konkretnie na pytania Maćka:
Wydaje mi się, że przepaść pomiędzy modelem analitycznym a projektowym nie powinna być problemem dzięki zastosowaniu starego indiańskiego triku architektonicznego - warstw.

Patrząc na projekt systemu i skupiając się na na warstwie logiki biznesowej powinniśmy widzieć nic innego jak analityczny model domenowy z UP. W DDD istnieje zestaw figur (zwanych building blocks) z których możemy budować model obiektów biznesowych. Tutaj ich opis.
A skupiając się na warstwie logiki aplikacji powinniśmy widzieć analityczny model przypadków użycia.



Dzięki temu, że takie "szczegóły" jak persystencja i prezentacja są wydzielone do innych warstw, warstwy logiki są "czyste" i możemy ich projekt - przy odpowiednim nastawieniu - traktować również jako model analityczny.

Odnośnie drugiej części pytania Maćka - o rzeczywistość: udało mi się ustalić dobre zasady współpracy z "ekspertami biznesowymi". Mam do nich dobry dostęp i możemy wspólnie tworzyć dokumentację analityczną - która przy okazji jest projektem moich warstw logiki:) Czyli przysłowiowe 2 pieczenie przy 1 ogniu. Jest to dla mnie bardzo ważne, ponieważ jak wszyscy pewnie wiemy logiczne rozumowanie nie jest mocną stroną "ludzi biznesu";) O myśleniu obiektowym możemy zapomnieć. Często dokumenty przez nich tworzone nie nadają się do czytania - a co dopiero analizowania.
Czasem dochodzi do takiej paranoi, że ktoś pisząc wymagania biznesowe niemal "szyfruje" w dokumencie prostą i oczywistą treść. Później analityk systemowy/projektant/programista musi to odszyfrowywać i domyślać się o so choszi.
Do tego nie każdy potrafi sensownie napisać parę stron, akapit czy choćby jedno zdanie! Pojawia się też problem kognitywny. Ja na ten przykład preferuję podejście top-down. Najpierw muszę uchwycić ogólną ideę a później mogę zgłębiać szczegóły. Krew mnie zalewy gdy muszę czytać czyjeś polucje, które akurat powstały w odwrotnym toku rozumowania.

Natomiast pismo obrazkowe (UML) ma to do siebie, że ciężko nabazgrać coś co jest kompletnie bez sensu. A w razie czego od razu widać:) Oczywiście sam fakt stosowania jakiegoś formalizmu nie implikuje, że dokumentacja musi mieć jakąkolwiek wartość oprócz dupochronu w razie szukania kozłów ofiarnych. O jakości dokumentacji stanowi tak na prawdę ilość pracy umysłowej włożonej w analizę.

Dlatego o wiele lepszym podejściem jest wspólne (ekspert biznesowy + analityk systemowy) tworzenie dokumentacji od razu w UML. Ekspert mówi, a analityk rysuje. Analityk pyta, ekspert odpowiada, analityk poprawia rysunek. Oczywiście nie zawsze mamy taki komfort.

//====================
Gdy się przyjrzeć z dystansem założeniem UP czy DDD można dojść do wniosku, że jest nic innego jak zestaw racjonalnych wytycznych. Są to oczywiste oczywistości, na które każdy kto jest skłonny do odrobiny refleksji wpadły sam - prędzej czy później. UP czy DDD jedynie zbierają i strukturyzują zestaw intuicyjnych zasad.

Aż dziw bierze gdy się zastanawiam jak w ogóle mogłem postępować (będąc zmuszanym) im wbrew i mało tego - coś tam powstało i nawet jakoś działa:P
Biedni spadkobiercy spuścizny...

5 komentarzy:

Tomek pisze...

Ach, jak pięknie to brzmi :) Jednego Eric chyba nie przewidział: pomysłowości marketingowców.

Od jakiegoś już czasu borykam się właśnie z problemem model domenowy = projekt obiektów biznesowych. Aplikacja, którą projektuję, podzielona jest na moduły. Moduły te mają różną odpowiedzialność ale niestety opierają się na jednym modelu domenowym. Żeby całkiem skomplikować zadanie dodam, że moduły te mają być kompletnie niezależne.

Jaki problem powstaje? Okazuje się, że jeśli chcemy pracować na jednym modelu domenowym w dwóch modułach to powodujemy "zaśmiecenie" obiektów biznesowych niepotrzebnymi funkcjonalnościami. Wynika to z faktu, że każdy moduł realizuje tylko jakąś część domeny. Możemy oczywiście taki pełny model przykryć warstwą i pozostawić tylko te metody, które w danym module mają sens. Ale zaraz przypominamy sobie, że przecież moduły są niezależne zatem nie wszystkie są tworzone na raz a stąd można wnioskować, że funkcjonalności "pełnego" modelu domenowego wcale nie są pełne (kiedy stworzymy kolejny moduł z kolejnymi funkcjonalnościami pozostałe, już wypuszczone moduły miałyby tylko fragment funkcjonalności i wszystko by się rozjechało - komunikacja oparta na różnych modelach domenowych itp.)

Jak na razie zastanawiam się nad rozwiązaniem polegającym na pozostawieniu modelu domenowego anemicznym. Każda funkcjonalność implementowana byłaby w kolejnej warstwie - obiekt biznesowy z danego modułu opakowywałby obiekt stworzony na podstawie modelu domenowego i miałby dodatkowo zdefiniowane funkcje odpowiednie dla danego modułu. Niestety to rozwiązanie też nie jest idealne - co ze wspólnymi metodami (oczywiście wspólnymi dla aktualnych modułów bo o innych nic nie wiemy)? Implementować je kilkakrotnie?

Zdaje się, że nie jest to standardowy przykład UP-DDD w Akcji. Sławku, widzisz jakąś prostą linię obrony dla unifikacji w tym przypadku? Może czegoś nie rozumiem i sam się zapędziłem w kozi róg?

Maciej Zubala pisze...

Na wstępie chciałbym podziękować Sławkowi za odpowiedź z dedykacją ;)
A co do problemu Tomka, to ja bym próbował go rozwiązać tak, że wszystkie moduły korzystają z jednego MODELU, który jest wspólny dla całego systemu. Moduły będą w tym wypadku FASADAMI modelu, które same w sobie nie zawierają logiki biznesowej lub zawierają jej śladowe ilości i pobudzją tylko MODEL do życia. Gdy teraz chcemy dorzeźbić nowy moduł to dorzucamy jego logikę biznesową do modelu i piszemy odpowiednią fasadę - być może w wyniku zmian w modelu trzeba będzie zaktualizować starsze moduły/fasady. Aby teraz wszystko się nie "rozjechało" u naszych klientów wprowadziłbym jakiś mechanizm wersjonaowania i updatów modelu i modułów.
Nie wiem czy to co napisałem ma sens, bo zawsze jechałem na "anemicznym modelu" ;), więc możecie mnie zbesztać jakby to były wg Was głupoty.

pozdro

Sławek Sobótka pisze...

Maciek - z tego co się orientuję, to ktoś Tomkowi wygenerowany problem w trybie komplikatora (http://art-of-software.blogspot.com/2008/09/komplikatormodeon.html) i nie będzie go tak łatwo dało się rozwiązać:/

Jego moduły to prawdziwe moduły - nie tylko widoki pogrupowane tak aby użytkownicy z jednego działu nie widzieli ekranów użytkowników z innego działu.

Wymaganie jest takie, że moduł ma być niezależną i wymienialną częścią. Musi dać się włączyć i wyłączyć oraz działać w różnych wersjach. Po prostu plugin.

Teraz gdyby kod modułów był wspólny to nie byłby niezależny i powyższe założenia szlag by trafił:/

Ja bym kombinował jakoś w tą stronę, że każdy moduł na swoje encje (z logiką biznesową) i inne moduły jej nie widzą. Bo czy tak na prawdę jakaś logika będzie wspólna (kros-modułowa?). Chyba nie. A jeżeli tak wyjdzie, to jeden moduł powinien prosić drugi o wykonanie logiki, która jest do niego przepisana. Modułu powinny się komunikować przy pomocy jakiś DTO ale nie przy pomocy obiektów z logiką biznesową.

Sławek Sobótka pisze...

"każdy moduł na swoje encje (z logiką biznesową)"

Pisząc to miałem na myśli, że przykładowo klasa Pacjent występuje w kilku modułach, ma różne atrybuty i różne metody biznesowe relewantne dla kontekstu danego modułu.

Ewentualnie można myśleć o module CORE ze wspólnymi klasami bazowymi... ale pamiętaj, że dziedziczenie to ZUOOOOO

Maciej Zubala pisze...

Tak sobie myślę i myślę nad tym problemem Tomka w przerwach podczas rzeźbienia formularzyka ;) i wydaje mi się, że te moduły to są po prostu u Tomka byty biznesowe - skoro są częscią wymagań, więc powinny mieć odzwierciedlenie w modelu zgodnie z założeniami DDD. Zatem encja PacjentZModuluA to będzie co innego niz encja PacjentZModuluB ;) Być może obydwie będą korzystać z jakiegoś PacjentaZModuluCore - oczywiście na zasadach asocjacji ;)
Żeby wszystko było wymienialne to trzeba by to jakoś powersjonować i popakować w osobne jarki, ale to już chyba jest temat na dyskusje o architekturze wdrożeniowej.