poniedziałek, 30 marca 2009

Inteligientny projekt



Ile kosztuje słaby i nieprzemyślany projekt?
Na początku wydaje się być kusząco tani - kokodżambo i do przodu.

Mimo iż dużo pisze się o zaciąganiu długu technicznego, o tym, że każda prowizorka i rozwiązania typu "na pałę" lub "byle jak - byle szybko" trzeba spłacić z nawiązką, to jakoś nie zawsze dociera to do świadomości.

Nudzenie o zgubnych skutkach niestosowania się np do paru prostych zasad GRASP czy SOLID jakoś nie jest zbyt przekonujące - chyba jest zbyt abstrakcyjne. Obiektowe filozofowanie typu DDD ustępuje miejsca metodykom RÓB i WHISKY (Why the Hell Isn't Somebody Koding Yet).

Aby zejść z poziomem abstrakcji i unaocznić problem w namacalny sposób zobaczmy na banalnym przykładzie pojazdu ile może kosztować (nie tylko pieniędzy ale i czasu oraz nerwów) słaby projekt.

Weźmy taki oto trywialny problem jak wymiana żarówki przedniego światła mijania w naszym modelowym systemie, którym niech będzie pojazd osobowy pewnej szwedzkiej marki, produkowany w duńskiej fabryce na amerykański rynek lecz użytkowany w centralnej europie.

/*
* Śpieszę wyjaśnić nielicznym paniom
* czytającym tego bloga, czym dla samca
* jest wymiana żarówki w jego własnym samochodzie.
* Otóż jest czymś co robimy SAMI - jest to
* punkt honoru.
* Zwracanie się z prośbą o pomoc do innego samca
* w tej jakże błahej sprawie
* jest czymś tak żenującym
* jak prośba o podtarcie tyłka.
* (Metafora może niezbyt elegancka, ale niestety
* nie znajduję w swej prostackiej głowie
* niczego co lepiej oddawało by wagę problemu)
*/


Zaczyna się od tego, że system logów wyświetla na konsoli informację sygnalizującą błąd lewego światła mijania. Hmmm mamy system autodiagnostyczny - pewnie zaszyli testy integracyjne w bootloaderze. Naajz, zapowiada się bułka z masłem.

Rozpoczynamy podejście do problemu jak rasowy informatyk. Nie informatyk, informatyk to obraźliwe słowo - coś jak konował albo znachor. Jak programista, tudzież developer lub inżynier oprogramowania. No więc zaczynamy od dokumentacji.

Dokumentacja jest co prawda nieco lakoniczna w temacie interesującej nas wymiany żarówek, ale w sumie to dobrze. Tu nie może być przecież wielkiej filozofii; cieszymy się wręcz, że nie mamy zbyt wiele do czytania - czarno na białym, jeden rysunek, jedno zdanie. Proste. Taką dokumentację to ja lubię... ukazuje sedno problemu, bez zbędnego kontekstu. Heh to musi być proste jak instalacja windowsa.

Pierwsze podejście do pracy na systemie. Dokonujemy wstępnych oględzin interfejsu użytkownika i już po paru próbach bezbłędnie lokalizujemy reflektory - na których to (jak wynika z dokumentacji) mamy za zadanie wykonać prace związane z maintenance. Właściwie to gwoli ścisłości interfejs użytkownika jest w środku. Na zewnątrz jest... hmmm powłoka (shell) mająca na celu separację użytkownika od podmuchów wiatru tudzież strug wody, która nie może się oprzeć sile grawitacji. Powłoka ma również na celu wywieranie wrażenia na blacharach - "osobnikach płci żeńskiej, dobierający sobie partnerów do prokreacji przez pryzmat samochodu jakim się poruszają" (że pozwolę sobie zacytować za nonsensopedią).

Powłoka spełnia zatem zadanie anticorruption layer (dosłownie chroniąc wnętrze przez rozkładem polegającym np na rdzewieniu) jak i warstwy servisów.

Zajrzyjmy jednak pod maskę, czyli warstwę niżej. Pięknie...cóż za separacja modułów. Niestety tylko z prawej strony. Coś co na pierwszy rzut oka wydawało się eleganckim warstwowym systemem z separacją modułów, po dokładnych oględzinach okazało się chaotycznym układem.

O ile instancja modułu oświetlenia zainstalowana prawej strony wydaje się być otwarta na prace utrzymaniowe to z lewej natomiast mamy kłębowisko spaghetti w okolicach modułu wyświetlania fotonów w kierunku jazdy. Do tego stwierdzamy silny coupling interesującego nas modułu z modułem chłodzenia - a dokładnie chyba z filtrem powietrza.

Co do cholery ma filtr powietrza z reflektorem?!? Kto scalił je tak blisko, że nawet mała rączka junior programera nie wcisnęłaby się aby sięgnąć do interfejsu żarówki - że o jej implementacji nie wspomnę! Spójrzmy jeszcze raz do dokumentacji. O ironio! Radość sprzed paru akapitów wywołana prostotą dokumentacji obraca się w rozpacz. Na tych cholernych rysunkach nie ma pudła z filtrem obok reflektora! Jak się dobrać do żarówki? Teraz przydało by się nieco więcej kontekstu w dokumentacji. Co robić? Aaaaa!

No ale może da cię coś obejść. Przecież nie pojadę z tym do warsztatu. Wyśmieją mnie. Minęło już 15 minut, czas na wunderwaffe - dekompilator, któremu nic się nie oprze - KOMBINERKI. Wspaniałe niskopoziomowe narzędzie, które obejdzie wszystkie zabezpieczenia. Metoda brute force. Wyciąganie flaków z core silnika aby zrobić nieco miejsca na ręczne prace utrzymaniowe. Hmm na moje oko jednak aby odłączyć moduł filtru powietrza należy iść dalej po zależnościach i wymontować połowę silnika. A przecież obok jest tyle miejsca. Nie możliwe aby zrobili to perfidnie - przecież to taka pożąda firma;)

Kto to projektował?!? Inżynier praktykant?

Sam tego nie zrobię, potrzeba guru od modułów oświetlenia z biegłą znajomością architektury (chyba burdelu) silników. Co za wstyd... może by tak wysłać żonę...

Ale na guru trzeba pewnie czekać w kolejce. I koszt będzie 10 razy większy niż samej żarówki. Ale jak mus to mus - przecież żadna blachara nie zainteresuje się systemem z niesymetrycznym UI.

Guru w pocie czoła przedarł się przez splątane niby-warstwy i po godzinie wymienił instancję żarówki oraz skompilował wszystko i zrobił deploy. Jeszcze tylko szybki test jednostkowy (szkoda, że po deployu:P) - tak dla formalności i bugfix można uznać triumfalnie za zakończony. Czas na commit faktury... Cholera - czerwony pasek! Yyyy to znaczy szara ściana - tzn żarówka nie świeci. Ponowna dekompilacja. Na szczęście udało się wykryć buga - spalona kostka. Szkoda, że w systemie występuje ona w egzotycznej wersji HB4 - jesteśmy uzależnieni od jednego dostawcy:/

Podsumowując
problem: wymiana 1 żarówki
czas: 2h
koszt: przemilczę
przewidywane problemy: wymiana 3 innych żarówek w lewym reflektorze w najbliższym czasie oraz związane z tym poczucie beznadziejnej bezradności:)


//===========================
Oczywiście nie wymagam absurdu polegającego na tym, że każdy kawałek systemu będzie niezależnym modułem czekającym na łatwą podmianę. Core to core - niektóre kawałki są silnie zespojone bo taka jest natura problemu - jestem w stanie to zrozumieć. Nie potrzebuję przecież zbyt często wyjmować skrzyni biegów, ale żarówka - przecież to podstawa.

Nie wymagam też niczego szczególnego typu otwartość na rozbudowę (np możliwość wymiany reflektorów na lasery służące do strącania pojazdów poruszających się nieskrajnie prawym pasem;). Chodzi o zwykłe i podstawowe czynności maintenance.

Chyba, że biznes polega na serwisie:)))

środa, 18 marca 2009

Piękny umysł

W dzisiejszym poście będę chciał zachęcić grono ponad pół tysiąca czytelników do zainteresowania się czymś z goła innym niż dwurdzeniowa maszyna z zainstalowanym jakimś systemem operacyjnym a na tym JVM i server JEE. Jest coś, co może okazać się jeszcze ciekawsze - Mózg.

Do napisania posta zainspirowała mnie wizyta na Czwartych Dniach Mózgu. Przy okazji zachęcam wszystkich mieszkańców naszej krainy pszenicy i buraka do skorzystania z okazji i zawitania na konferencji. Mimo backgroundu uczelni organizującej konferencję zapowiada się na prawdę interesująco;)

Dziś na ten przykład na warsztacie Dr Krzysztofa Stachyra (muzykoterapeuty) dowiedziałem się wielu ciekawostek na temat oddziaływania muzyki na mózg. Pouczające było doświadczenie różnic percepcji muzyki przez indywidualne jednostki jak i różnic zależnych od np pory dnia. Kilkadziesiąt osób na sali, słuchamy fragmentu utworu po czym okazuje się, że dla części uczestników był on relaksujący a dla innych irytujący, dla jeszcze innych pobudzający. Niesamowite... a człowiek dziwi się, dlaczego inni krzywią się gdy puszczam im Boysów;)

Jaką muzyką uraczyć kogoś kto jest nastawiony agresywnie - lub ogólnie: pod wpływem stresu? Spokojną i relaksacyjna? Okazuje się, że jest to zła odpowiedź:P Wyjaśnienie niesie nam neuropsychologia. Lepiej jest zastosować muzykę, która podniesie poziom stresu - np. wymagającą skupienia uwagi lub stymulującą. Doprowadzi do do szybkiego osiągnięcia maximum napięcia, po którym następuje "przebranie się miarki" i ulga. Przy innym podejściu wysoki (ale nie max) poziom utrzymywałby się stosunkowo długo.


Ciekawe doświadczenie pracy własnego mózgu przeżyła Jill Bolte Taylor (aktualnie prezentacja #2 na TED). Sama będąc neuro-naukowcem przeżyła udar mózgu - opowiada o swych doświadczeniach gdy jedna z jej półkul wyłączała się co chwilę. Dzięki nabytej wiedzy była świadoma tego co się z nią dzieje i może teraz opowiedzieć o swych doświadczeniach. Heh dzięki temu, że jest "z branży" nie myli swych doświadczeń z nirvaną, objawieniem czy porwaniem przez obcych;)

//==========================

Na zakończenie aby nawiązać jednak do IT pozwolę sobie na pewną refleksję. Często porównuje się komputer do mózgu. Mówimy, że jakaś maszyna jest mózgiem systemu. Albo odwrotnie: trenując jakiś kombat chronimy gardą nasz procesor:)

Ludzie zawsze (tzn kiedy już wiedzieli po co między uszami jest ta galareta) porównywali umysł do aktualnie najwybitniejszego wynalazku cywilizacji... W starożytności porównywano go do katapulty strzelającej myślami, później do pompy pompującej myśli. W epoce pary oczywiście porównywano umysł do do maszyny parowej tłoczącej myśli. Teraz mamy pokraczne analogie do komputera.

Ale czy to na poziomie półprzewodników (złączy NP, tranzystorów), czy to na poziomie bramek logicznych, czy nawet na poziomie abstrakcyjnej maszyn Turinga nie istnieje znaczące podobieństwo do działania mózgu biologicznego. Nawet sztuczne sieci neuronowe są bardzo uproszczonym modelem - wręcz trywialną karykaturą (chociaż być może nowe, zaawansowane sieci są bardzie adekwatne - nie wiem).

Więcej na temat mylnej metafory komputer-mózg w książce Istota inteligencji autorstwa Jeffa Hawkinsa - faceta, który wymyślił algorytm rozpoznawania pisma używany na wszystkich urządzeniach przenośnych (a cały zysk "zainwestował" w badania mózgu). To się nazywa prawdziwa pasja - piękny umysł próbujący zgłębić sam siebie...

poniedziałek, 9 marca 2009

Undergroundowy Domain Driven Design

"Kraków - dawna stolica Polaków"

W sobotę odbyła się w Krakowie konferencja 4develoeprs, na której to byłem miałem (że pozwolę sobie napisać w staropolskiej składni zaprzeszłej będąc natchnionym pobytem w mieście poetów i masakrycznych dziur w asfalcie) przyjemność przedstawić krótką prezentację.

Tym, którzy zastanawiają się jakim cudem pojawiłem się na salonach obok takich znamienitości jak Adam Bien czy Neal Ford śpieszę wyjaśnić iż chodzi o Java Underground.

JU, którego organizatorem był Grzegorz Duda polegał (w skrócie) na przedstawieniu prezentacji w ciągu 5 minut, a brali w nim udział: Adam Bien (EJB), Bartek Kuczyński (JavaFX), Szymon Jachim (Scala), Bartek Chrabski (Jazz) no i ja (o Jacku L. który nie dotarł nie wspomnę;)

Aby zmieścić się w konwencji jako temat wybrałem sobie pewne "undergroundowe" aspekty architektury pojawiające się w DDD. Jako, że temat spotkał się z pewnym zainteresowaniem, a ja w ciągu pięciu minut byłem w stanie wydobyć z siebie jedynie turbo-bełkot, rozwinę w tym poście nieco bardziej każdy ze slajdów.



O ile samo podejście DDD nie jet już undergroundem to implikacje wynikające z podążania za nim można już do undergroundu zaliczyć. Ortodoksyjne przestrzeganie zasad DDD prowadzi do pojawienia się na poziomie projektu i implementacji pewnych konstruktów, które mogą się wydawać obrazoburcze, szczególnie w porównaniu z tradycyjnym podejściem, lansowanym przez różnego rodzaju frameworki w różnego rodzaju tutorialach.



No więc właśnie... silnie zakorzeniona tradycja każe umieszczać algorytmy w procedurach a dane w strukturach danych.



Źródła tej tradycji sięgają co najmniej szalonych lat '70 (pewnie i wcześniej), kto programował w turbo pascalu ten zapewne widzi analogię pomiędzy servisami a procedurami oraz encjami a rekordami.

Tradycja proceduralna jest mocno ugruntowana i nie można jej kwestionować. Doskonale nadaje się do modelowania tych domen, które z natury są proceduralne. Niestety nie sprawdza się zbyt dobrze w przypadku problemów z domen o naturze obiektowej.

Warto zastanowić się dobrze w początkowej fazie projektu nad naturą domeny. Nie warto walczyć z naturą - nikt jeszcze tej walki nie wygrał. Wszyscy wiemy czym kończy się proceduralne podejście stosowane do skomplikowanych (nie znaczy dużych!) domen... Prędzej czy później pojawia się ON. Wyłania się z plątaniny zależności i powielonej logiki... Przeklęty demon... Potwór Spaghetti.

Właściwie to w klasycznym podejściu będzie to Potwór Lasagne (Spaghetti z warstwami;)



Koncepcja DDD pojawiła się jako odpowiedź na problemy z komplikacją logiki biznesowej. DDD zakłada, że warstwa logiki domenowej jest najważniejszym elementem systemu i jednocześnie najtrudniejszym do opracowania. Jest to serce systemu, ale serce bardzo kruche. Niepoprawny model nie jest odporny na zmiany (której jak wiemy z doświadczenia są pewne jak śmierć i podatki - podatki to chyba nie u nas).

DDD nie jest żadną technologią, platformą ani frameworkiem. Jest to jedynie zbiór zasad, wytycznych, wzorców i dobrych praktyk, których stosowanie ma zapewnić zaprojektowanie porządnego modelu domeny.

DDD to dosyć obszerna "filozofia", natomiast na potrzeby przykładów undergroundowych architektur zobaczymy tylko mały jej fragment. Przy okazji warto przypomnieć, że DDD aplikuje się raczej do złożonych problemów. Pytanie tylko, kto potrafi przewidzieć gdzie zabrnie projekt - już parę razy spotkałem się z projektami, które na początku wydawał się "szybką akcją bydgoskiej milicji". Nota bene: proste problemy powinno rozwiązywać się chyba w Excelu; ostatecznie w Accesie;) Ale jeżeli już czujemy silną wewnętrzną potrzebę aby wydurniać się z programowaniem prostych problemów i nie chcemy robić tego proceduralnie to zawsze można skorzystać z podejścia DDD Lite.



DDD tak na prawdę nie jest niczym nowym. To po prostu zestaw dobrych praktyk opartych na starych dobrych (ale nie tak starych i nie tak dobrych jak Turbo Pascal;) koncepcjach obiektowej analizy i projektowania. Wystarczy po prostu ich nie negować i nie programować wbrew nim - a jest to konieczne aby osiągnąć pewien cel...

DDD zakłada unifikację modelu analitycznego i projektowego, o którym już pisałem: UP-DDD in Action: Malutka Teoria Unifikacji. Cały zespół, począwszy od analityków, poprzez projektantów aż po koderów powinien posługiwać się wspólnym językiem opisującym domenę - zwanym w DDD Ubiquitous Language. Niestety aby było możliwe dosłowne zaprojektowanie pomysłów analityka musimy posługiwać się pełnym wachlarzem środków wyrazu języków OO. Procedury i encje nie wystarczą - o tym dalej przy okazji archetypów.




DDD zakłada modelowanie warstwy logiki domenowej przy użyciu pewnych standardowych klocków, zwanych Building Blocks. Jest to jedna z głównych koncepcji. Na pierwszy rzut oka może się wydawać, że jest ich całkiem dużo, ale każdy ma uzasadnienie swego istnienia wynikające z koncepcji Responsibility Driven Design, o której pisałem.



Artefakty już opisywałem, więc jedynie gwoli przypomnienia:

  • Encje w DDD nie są tym samym czym czym w standardowym podejściu. Encje modelują biznes więc posiadają odpowiedzialność - metody biznesowe.
  • Servisy w DDD nie są tym samym czym czym w standardowym podejściu. Serwisty z tej warstwy enkapsulują logikę, której nie można w sensowny sposób przypisać do odpowiedzialności żadnej z encji.
  • Repozytorium jest abstrakcją persystencji - pamiętajmy, że w nietrywialnych projektach części agregatów (grafów encji) mogą pochodzić z różnych źródeł (np część z bazy, część z web servisu, a inna część z LDAP)

W ortodoksyjnym podejściu encje nie posiadają nawet getterów i setterów. To jest totalny underground w porównaniu z klasyką. Niewielu programistów potrafi to zaakceptować i dla tego chowają się za murem dysonansu kognitywnego.

Pewnie zastawiasz się: jeżeli nie ma nawet getterów/setterów to w jaki sposób wyświetlić encję na formularzu? Albo jak ją zmienić? Odpowiedź już niedaleko...



Nieograniczanie się do anemicznych (pozbawionych odpowiedzialności) encji oraz prymitywnych servisów ma kolejną niebagatelną zaletę. Posługiwanie się bogatym zestawem Building Blocks oraz korzystanie z bogactwa technik OO pozwala niemal dosłownie implementować zaawansowane i złożone modele analityczne. Na przykład modele wyprowadzone z archetypów. O archetypach już w niedalekiej przyszłości pojawię się parę postów na tym blogu, ponieważ IMHO jest to zdecydowanie ważniejsze niż kolejny gówniany framework webowy.

Póki co przyjrzyjmy się przykładowemu modelowi użytkowników na powyższym slajdzie... Piękny, prawda? Niestety nadaje się jedynie na przykłady w książkach lub jako model w projektach "Hello World".



Chcąc nieco bardziej profesjonalnie zaprojektować model np. użytkowników możemy
a) pomyśleć - co nie gwarantuje sukcesu
b) skorzystać z archetypu o nazwie "party" ("uczestnik", nie "impreza")

Tak jak wspomniałem o archetypach będzie później, ale zwróćmy uwagę na (pozorną) komplikację powyższego modelu. Jest fajny, ponieważ można w nim przedstawić dowolną relację pomiędzy dowolnymi uczestnikami (np ja mogę być klientem jakiejś firmy jak i jej pracownikiem; firma ta może być klientem mojej żony; itp).

Ale życzę powodzenia implementując ten (czy inny) zaawansowany archetyp przy pomocy klasycznego podejścia - anemicznego modelu. Widzicie już oczyma wyobraźni te łańcuszki 15 kropek i getterów - train wreck.

Z wykorzystaniem technik DDD możemy w sposób naturalny (OO) i bezproblemowy zaimplementować ten model a całą komplikację ukryć w hermetycznym agregacie (grafie encji), który operuje na swej wewnętrznej skomplikowanej strukturze poprzez metody biznesowe.



Undergroundowe w DDD jest również podejście do zależności. Otóż zależności na poziomie tej samej warstwy nie są niczym złym. Przecież zależności odzwierciedlają model biznesu, więc nie ma potrzeby aby się wydurniać z wstrzykiwaniem zależności czy tworzeniem na siłę interfejsu dla każdego komponentu biznesowego. Wiem, że niektóre platformy od niedawna;) obsługują Wstrzykiwanie Zależności i wymagają wszędzie interfejsów, ale do wszystkiego trzeba podejść z rozsądkiem.

Natomiast zależności modelu domenowego od klas infrastruktury są niepożądane. Nawet gdy klasy te są przykryte interfejsem - to nic nie zmienia, może co najwyżej poprawia nam samopoczucie.

Jedyna techniczną zależność modelu biznesowego jaką dopuszcza DDD jest zależność od szyny komunikatów. Obiekt biznesowy w momencie, gdy poczuje taką potrzebę może co najwyżej stworzyć komunikat i puścić go do pipe (która zawiera zwykle filtry). Jest to architektura pipeline.



Klasyczne podejście warstwowe również nie sprawdza się zbyt dobrze w przypadku DDD. Na powyższym slajdzie widzimy 2 możliwości:

Możemy "podpinać" obiekty biznesowe (te, które mają jakiś stan) pod GUI. Niestety wiąże się to z pewną niezręcznością: GUI "widzi" metody biznesowe. Poza tym obiekty biznesowe muszą oferować gettery i settery w celu dokonywania modyfikacji na nich. Niestety narusza to obiektowy paradygmat enkapsulacji, czyli prowadzi do pornografii obiektowej - odkrywania wewnętrznych struktur.

Podejście to ma jeszcze jedną wadę. Implementowanie setterów pozwala na istnienie obiektu w stanie niespójnym. Przykładowo zmieniając setterami encję Adres możemy zmienić miasto ale nie zmieniać kodu pocztowego:P
Mimo tych wad, podejście tego typu jest często stosowane w "prostych" przypadkach.


Strzałka po prawej stronie diagramy prezentuje inne podejście. W warstwie aplikacji obiekty biznesowe są przepakowywane do/z obiektów transportowych DTO. Wiem, że przepakowywanie obiektów kojarzy się z robotą szympansa, ale dzięki temu możemy uniknąć powyższych wad i niezręczności. Zaraz zaraz... jak pobrać stan obiektu biznesowego gdy nie ma on getterów? Myśl obiektowo; nie pytaj, wydawaj rozkazy! Oto rozwiązanie.

Podejście opierające się na przepakowywaniu ma jednak wadę polegającą na tym, że zupełnie niepotrzebnie pobieramy z repozytorium nadmierną ilość danych - ponieważ dany obiekt transferowy (potrzebny w danym przypadku użycia np. dla danego ekranu) może potrzebować znacznie mniej danych niż zawiera np agregat.



O ile powyższe podejścia do warstw są zadowalające, to prawdziwy underground pojawia się gdy uznamy, że jeden wymiary (wysokość warstw) nie wystarcza i potrzebujemy drugiego - heh to stary trik fizyków. Gdy brakuje im wymiarów aby upchać swe teorie to zakładają istnienie nowych -stąd w różnych odmianach Teorii Strun 11 a nawet 26 wymiarów czasu i przestrzeni;P

Ortodoksyjna architektura DDD wymaga istnienia 2 stosów. Jeden do "prymitywnych" operacji CRUD, drugi natomiast do obsługi rozkazów dla modelu domenowego. Podejście to zapewnia nam utrzymanie eleganckiego stylu Command-Query Separation. Stylu w którym polecenia odczytu stanu i polecenia zmiany stanu są jasno i przejrzyście odseparowane. Posiadanie osobnych klas manipulacyjnych stanem pozwala łatwo audytować i kontrolować ten kod.

Zauważmy, że stos DDD jest tylko do zapisu. Dokonujemy w nim zmian wysyłając komunikaty z poleceniami (wzorzec command). Obiekty biznesowe mogą ew. wygenerować swoje komunikaty i umieścić je w pipeline. Natomiast klient może odpytać o stan jedynie poprzez stos CRUD wysyłając komunikat z kwerendą.

Dodatkowo stos CRUD może zwracać Encje (nie biznesowe a anemiczne - czyli "normalne") będące obiektami transferowymi "szytymi" na miarę.

Separacja przetwarzania logiki od magazynu danych pozwala tworzyć architektury przygotowane na masowe obciążenie i skalowanie - DDDD (4xD to nie pomyłka, ale Distributed DDD). Architektury, gdzie liczy się coś więcej niż procedury i encje. Architektury, w których 2 stosy odpowiadają za osobne moduły z osobnymi magazynami danych w szczególności. Magazynami, które mogą być optymalizowane względem czasu wykonania (stos Command) i czasu wykonywania przekrojowych raportów (stos Query). Magazyny, które mogą asynchronicznie się komunikować i synchronizować.

Ale to temat na osobnego posta... Zainteresowanym polecam wywiad z Gregiem Youngiem - jednym z guru DDD.

//=========================

Tak wyglądają lekkie modyfikacje standardowych architektur warstwowych dopasowane do DDD i DDDD. W świecie Javy jest to jeszcze underground...

Ostatnią z prezentacji, która zakończyła moją sesję na 4developers była "Understanding Distributed Messaging Systems and Where They Fit" Raymonda Lewallena
ze ścieżki .NET.

Raymond opowiadał o podstawowych architekturach dużych i skalowalnych systemów rozproszonych opartych o komunikaty. Jakież było moje zdziwienie gdy Raymond wspomniał nagle coś o Agregacie, później o tym, że wysyła on komunikat do pipleina aż wreszcie zobaczyłem diagram architektury 2-stosowej. Dla niego było to takie oczywiste i naturalne, że używa DDD... nawet o tym nie wspomniał i nie robił żadnego wprowadzenia... po prostu standard:)

Chyba chłopaki od .NET wiedzą w co się bawić;P

niedziela, 1 marca 2009

W co się bawić?

"W co się bawić? W co się bawić..." śpiewał (a właściwie - nie oszukujmy się - mówił przy akompaniamencie muzyki) Wojciech Młynarski - pierwszy polski raper.

Jest to pytanie, które często sobie zadajemy - a przynajmniej instynkt samozachowawczy powinien je nam podsuwać co jakiś czas.

Zakładając, że programujesz w Javie oraz celujesz w systemy "korporacyjne" (jednocześnie pomijając dyskusję nad tym wyborem) masz do wyboru szereg kombinacji elementów typu: JEE, Spring, Swing, SWT, JPA, Hibernate, Seam, JSF, Tapestry, Wicket, GWT, Flex... (samych frameworków webowych będzie kilkanaście - nie licząc wynalazków typu krzak). Do tego dochodzi cała masa bibliotek - po kilka na każdy problem (xml, javascript, ajax, security,..). Nie zapominajmy o narzędziach, serwerach i najlepszym pretekście do toczenia świętych wojen - IDE.

Mało tego, nie ograniczajmy się tylko do Javy - książki typu "Pragmatyczny programista" zalecają przecież aby co najmniej raz w roku uczyć się nowego języka:)


Oczywiście, wiedzy nigdy za wiele. Teoretycznie każde nowe skojarzenie może potencjalnie poszerzyć horyzont, otworzyć gdzieś tam w zakamarkach mózgu nową furtkę, odblokować nowy sposób myślenia i zaowocować olśnieniem. Ale podejdźmy do problemu racjonalnie. Zakładając jednak, że czas na research mamy ograniczony musimy dokonywać wyboru - czym się zająć, co zgłębić, co przejrzeć a co pominąć. Tu pojawia się niestety paradoks wyboru grożący zawieszeniem niczym ten przysłowiowy osioł, któremu w żłoby dano.


Spróbujmy jednak spojrzeć z innej strony - poprzez analogię do władania językami obcymi: co jest ważniejsze, to ile znasz języków; a może raczej to czy chociaż w jednym z nich masz coś mądrego do powiedzenia? Albo od innej strony: czy poliglota władającymi 5 językami jest w stanie stworzyć sonet choćby w jednym z nich?

Nieustanne miotanie się pomiędzy kolejnymi dostawami świeżego mięcha (nowe frameworki i biblioteki) może powodować ciągłe dryfowanie na drugim (z pięciu) poziomie kompetencji. Permanentne przebywanie na poziomie advanced begginer zwykle kończy się paskudnym schorzeniem - onanizmem technicznym. Zjawisko to po raz pierwszy zaobserwowano na osobnikach z branży fotograficznej, którym to wydaje się, jakoby odpowiednio duża ilość megapikseli gwarantowała wykonanie dobrego zdjęcia.


Wracając do naszego kontekstu: czy biegła znajomość API 15 frameworków GUI ma wpływ na jego estetykę, ergonomię, usability, intuicyjność i poziom autystyczności?

Dlatego na pytanie "W co się bawić?" odpowiadam zawsze, że najważniejsze są pewne koncepcje, które można sobie zrealizować przy pomocy dowolnych narzędzi. Ważniejsze od platformy technologicznej są: rzetelna analiza (np oparta o archetypy analityczne), giętka architektura, sensowny projekt (np oparty o DDD i wzorce).

Zastanówcie się czy Wasza nowa zabawka na prawdę rozwiązuje jakieś problemy - poza tymi, które sama stwarza.

//=====================

W wolnych chwilach szperam w poszukiwaniu materiałów na temat Domain Driven Design i niestety zauważyłem pewną niepokojącą prawidłowość. Przeważająca większość z nich pochodzi ze świata .NET!

Aby było ciekawej pamiętajmy, że książka Erica Evansa zawierała przykłady w Javie;P
Zwróćcie też uwagę, że nowe teksty Martina Fowlera są poparte przykładami w C#!

Zastanawiam się, czy nie jest przypadkiem tak, ze środowisko Javowe jest cały czas na etapie onanizmu technicznego spowodowanego zalewem mniej lub bardziej udanych narzędzi, bibliotek i frameworków, które po prostu rozpraszają uwagę od kwestii na prawdę istotnych.

W środowisku .NET natomiast wybór jest zdecydowanie uboższy, ale może dzięki temu można szybko skompletować skrzynkę z narzędziami i przejść na kolejne poziomy kompetencji. Pozwala to szybciej dojrzeć do na prawdę istotnych aspektów wytwarzania softu i wreszcie oddać się radosnemu filozofowaniu na przykład na tematy DDD:)

niedziela, 1 lutego 2009

Po czym poznac, że jesteś zbyt stary na SCJP?

Skończyłem właśnie czytać miłą książeczkę SCJP Sun Certified Programmer i chciałem się podzielić przemyśleniami (bo tak se czasem lubię trochę pomyśleć).

Optymistycznie nastroił mnie do niej fakt iż jej autorzy są współautorami lekkiej i przyjemniej (jednocześnie bardzo pouczającej) książeczki Head First Design Patterns - którą to na marginesie szczerze polecam. Jej komiksowa forma i humor osadzony w latach '50 może wydawać się dziecinna ale po przeczytaniu wstępu wszystko stanie się jasne - jeden z autorów zajmuje się neuropsychologią i kognitywistyką więc dobrze wiej jak powinno się uczyć dorosłych ludzi.

Z dorosłymi idzie ciężej niż z dziećmi bo zazwyczaj myślą samodzielnie - a czasem nawet krytycznie.


No ale wracając do SCJP... Do samej książki nic nie mam - autorzy starali się jak mogli aby czerstwy temat przedstawić w miarę przystępny sposób. Pozytywne są komentarze typu: "my jako autorzy mamy na ten temat inne zdanie, ale na potrzeby egzaminu musisz robić tak i tak". Każdy z 10 rozdziałów obejmuje pewne zagadnienie - na dosyć podstawowym poziomie, ale mimo tego czasem można dowiedzieć się czegoś nowego. Na zakończenie każdego z rozdziałów mamy pytania sprawdzające, które z założenia odpowiadają tym z właściwego egzaminu. Utrzymane są głównie w konwencji: masz tu kawałek "super-profesjonalnego" kodu, skompiluj w jednej półkuli mózgu i ew. uruchom w drugiej; możesz też jęknąć w razie wyjątku. Problem merytoryczny jest zwykle banalny a wręcz wypaczony, natomiast cały ciężar trudności leży po stronie składni upstrzonej kuriozalnymi identyfikatorami i perfidnych zagrywek służących odwróceniu uwagi.

Zacząłem się zastanawiać dla kogo i po co jest ten egzamin... Doszedłem do wniosku, że możesz być na niego zbyt stary gdy masz niektóre z poniższych objawów:




  • Zastanawiasz się gdzie są pogrubienia slow kluczowych - a nie ma...:P będziemy bawić się w sado-maso
  • Zastanawiasz się gdzie jest cholerny kompilator i podkreślenia błędów?!? - a nie ma...:P i nie będzie...:P już wiesz na czym ma polegać cała ta żałosna zabawa...
  • Zaczynasz się czuć jak student I roku na jakimś bezsensownym egzaminie z twierdzeń i ich dowodów
  • #$%^& idę pograć sobie na PS3
  • Zastanawiasz się gdzie jest dekiel który napisał ten kod? Tak się przecież nie programuje nawet w PHP w gimnazjum! Niech bym go tylko dostał w swoje ręce.
  • Zwolnic go natychmiast i zanotować nazwisko na liście ludzi, z którymi nigdy nie chcesz współpracować
  • Co to w ogóle za odpowiedź "G) Compilation fails"? To po cholerę komitujesz mi do poważnego testu niekompilujący się kod baranie jeden! W chyba każdej firmie dostałbyś po premii (a w pożądaj poleciałbyś na pysk) za choćby parę takich smrodków!
  • Tijaaa znowu pytanie z serii "wlałeś benzynę przez rurę wydechową. Co się stanie? Samochód pojedzie ale skręci w drzewo. Samochód nie odpali. Samochód wybuchnie." Nie wydurniaj się człowieku. No wlej tą benzynę jak biały człowiek do baku i nie zawracaj mi głowy zgadywanką co się stanie jak dodatkowo założsysz gacie przez głowę.
  • Zaczynasz rozumieć pojęcie "Code made in India"
  • Gryzie Cię poczucie tracenia czasu
  • Obawiasz się, ze jeszcze jeden rozdział i nabawisz się autyzmu
  • Zaczynasz żałować, ze nie masz tego autyzmu - wspaniałego schorzenia, które pozwala skupiać się godzinami na nieistotnych pierdołach i ignorować szerszy kontekst
  • Boisz się, ze nabyty autyzm mogą odziedziczyć Twoje dzieci (ku chwale wujka Suna)
  • Motyla noga! Gdybym był wzrokowcem a nie słuchowcem to może przynajmniej widziałbym te brakujące średniczki i nawiasiki:/
  • Dowiadujesz się, ze o wiele ważniejsze od projektowania i rozmyślania nad architekturą jest znajomość kolejności operatorów (nawiasy są dla cieniasów)
  • Dowiadujesz się, że enkapsulacja to jednak prywatne pola i publiczne gettery/settery
  • Dowiadujesz się, że w programowaniu współbieżnym najważniejsze jest kretyńskie zaprojektowanie wątków oraz to aby całość jakimś cudem się skompilowała. Teoria współbieżności jest dla dinozaurów programujących w ADA.
  • Zastanawiasz się jak mogłeś przez 6 lat programować w języku, którego jak się okazuje w ogóle nie znasz


//=====================

Jest tez druga strona medalu: możesz być zbyt młody na SCJP... Jeżeli dopiero zaczynasz przygodę z Javą to olej certyfikaty ciepłym moczem parabolicznym. Ciesz się życiem i programowaniem. Programowanie wcale nie musi być nudnym zajęciem polegającym na wyszukiwaniu literówek i analizowaniu jakiegoś gównianego kodu (no dobra, czasem jest). W tej zabawie chodzi na prawdę o coś zupełnie innego!

A jeżeli chcesz zwiększyć swojego skilla to przeczytaj sobie dwie doskonale książki:
- Efektywne programowanie w języku Java autorstwa samego Joshua Bloch - projektanta i autora dużej części klas biblioteki standardowej Java; książka o której to sam James Gosling powiedział, ze gdyby została napisane wcześniej to inaczej zaprojektowałby Javę;)
- Java. Potrzaski zestaw 50 na prawdę realistycznych i ważnych haczyków na które należy uważać (nie ma tam syfu typu brak średnika)

Jeżeli jeszcze Ci mało, to proszę:
- More Java pitfalls - druga część potrzasków
- interesuje cię programowanie współbieżne? bardzo dobra pozycja: Java. Współbieżność dla praktyków

Ucz się z tych pozycji, ale powtarzam: nie trać czasu na certyfikat! To w żaden sposób nie zrobi z Ciebie lepszego człowieka (ani programisty).

Później przyjdzie czas na ważniejsze zagadnienia: projektowanie (wzorce projektowe), architektura, analiza itd... Nie zapominaj też o takich "szczegółach" jak dogadywanie się z ludźmi (koledzy z teamu, klient). A średniki poprawia Eclipse;P

piątek, 30 stycznia 2009

ORM - o co ta walka?



Jako weteran hibernowania obiektów (w służbie od wersji 2.0) pozwolę sobie dziś na nieco prowokującego posta z lekkim dystansem do mainstreamu...:)

Pomiędzy światem relacyjnym a obiektowym istnieje oczywista niezgodność paradygmatów. Wynika z tego szereg (powiedzmy delikatnie) niezręczności - dosyć wyczerpująca lista tu: Object Relational Impedance Mismatch.

/*
 * Na marginesie dodam, że c2.com jest nieprzebraną
 * kopalnią wiedzy - co prawda ascetyczną,
 * ale pełną materiałów uzupełnionych
 * wnikliwymi przemyśleniami, polecam.
 */


Żeby wymienić te bardziej istotne "niezręczności":
- Enkapsulacja - a właściwie jej brak:) Nie ma to jak przykładowa kolekcja pozycji na zamówieniu: order.getItems() wybebeszona na wierzch aby można było ją radośnie modyfikować bez wiedzy obiektu order. Czy ktoś dziś pamięta jeszcze od dobrej praktyce zwracania Iteratora? W naszym przypadku mielibyśmy order.getItemsIterator(), który pozwala jedynie bezpiecznie przejrzeć pozycje. Natomiast jakakolwiek modyfikacja musi być wykonywana przez zamówienie, np: order.addItem(Item). Co na to daje? Ano obiekt zamówienie lepiej od nas powinien wiedzieć co z tym fantem zrobić (gdzie dodać, a może coś przeliczyć):P

Dobrze, że mapery od jakiegoś czasu wspierają prywatne pola bez getterów/setterów. Można coś w ten sposób powalczyć.

- Typy danych (grafy vs krotki) - w OO mamy wskaźniki/referencje, natomiast w świecie relacyjnym iloczyny kartezjańskie wartości. Krotki jak to krotki - nie zawsze muszą mieć sens;)

Przy okazji: czy ktoś może mi podać racjonalny powód, dla którego w JPA muszę zawsze używać słowa DISTINC w zapytaniach aby uchronić się przed produktem iloczynu? Przykładowo gdy chcę pobrać listę zamówień a wraz z nimi dociągnąć pozycje to otrzymam zduplikowane obiekty zamówień - z uwagi na kartezjan z pozycjami. Pokutuje tu niestety myślenie relacyjne. Przecież wydaję zapytanie o graf obiektów a nie o jakiś zakichany iloczyn!

- Dane/odpowiedzialność - myślenie relacyjne jest nastawione na dane, obiektowe powinno być nastawione na zachowanie.




Zatem wydawać by się mogło, że sensowne mapowanie relacyjno-obiektowe wymagać musi nie lada akrobacji umysłowych. Jednak nie koniecznie:) Zamiast rozwiązać problem (np uznając, że się nie da) udajemy radośnie, że wszystko jest ok:)

Standardowe wykorzystanie ORM jest zawieszone niejako pomiędzy dwoma światami ze swymi żałosnymi namiastkami obiektów zredukowanymi do karykaturalnych paczek danych - Anemic Model.

Nie należy oczywiście popadać w paranoję i dążyć do tego, że każdy kod musi być obiektowy. Jeżeli nie ma takiej potrzeby/możliwości to pewnie, że nie. Ale po co się wówczas wydurniać z całym tym mapowaniem, które i tak jest nieudolne? Czy zamiast sztampowej klasy Person z polem firstName nie wystarczy hehe result[35]? No dobra, to przegięcie - niech będzie resultMap.get(Fileds.FIRST_NAME)? Po co się wydurniać i oszukiwać, że mamy jakieś OO.
O co walczyć w przypadkach gdy kod (encji jak i cała reszta) i tak nie jest obiektowy lecz proceduralny?
Czy nie będzie to tylko niepotrzebny narzut pasujący jak kwiatek do kożucha - że zakończę kontrowersyjnie jak obiecałem;P

//==============================

Oprócz filozoficznych zagadnień pojawiają się również całkiem praktyczne:

- modelować począwszy od bazy czy warstwy obiektów domenowych? Ja mam swoje zdanie sprawdzone w boju: Życia nie oszukasz - jeżeli tylko jest taka możliwość zacznij od obiektów (mapery tutaj akurat robią dobrą robotę generując automagicznie bazę z klas - stosować tylko w fazie "szału tworzenia")

- W kontekście Doman Drive Design: metody biznesowe w encjach oczywiście nie są fajne, gdy chcemy przesyłać encje z/do klientów. Pytanie czy na pewno chcemy je tam przesyłać? Naiwne mapowanie (zamapowane wszystkie powiązania z bazy) pozwala dodając zamówienie dodać razem z nim gdzieś tam w niezachermetyzowanych bebechach encję nowego usera z fajnymi uprawnieniami:)
W małych systemach na pewno wygodnie jest posługiwać się encjami we wszystkich warstwach, ale w większych jednak oldskulowe DTO pozwoli ukryć strukturę modelu biznesowego.

niedziela, 11 stycznia 2009

Dystans

W życiu trzeba mieć dystans - to podstawa. W programowaniu też - szczególnie do tego co mówi klient:)

Dalsze me dywagacje pierwej podeprę takim oto małym przykładzikiem z frontu (projektu w jakim do niedawna jeszcze brałem udział):

Emulując analityka radośnie przeprowadzałem analizę małego modułu do zarządzania drukami ścisłego zarachowania. Z analizy biznesowej wynikało jakoby:

W jednym dziale panie pracują z DPDANPB_1 (Drukiem Potrzebnym Do Arcy Nudnego Procesu Biznesowego 1); druki przychodzą z drukarni do działu w paczkach po parę tysięcy sztuk; użytkownicy dzielą paczki na bloczki po kilkadziesiąt sztuk.

W innym dziale panie pracują z DPDANPB_2, które to przychodzą z drukarni również w paczkach - tym razem po kilkaset sztuk. Użytkownicy w tym dziale nie dzielą sobie druczków na bloczki - po prostu radośnie wykonują swą jakże smutną pracę używając druczków DPDANPB_2.

W obu przypadkach system musi ogólnie rzecz ujmując nadzorować druczki (zliczać, kontrolować kto komu ile po co wydał itp) z uwagi na to iż są one jak już wspomniałem "ściśle zarachowane" - cóż za małostkowość swoją szosą;)


Zatem na tak zwany rzut tak zwanego oka widać, że mamy do czynienia z taką oto strukturą (wybaczcie ASCII art, ale w niedzielny poranek nie chce mi się odpalać kombajnu - do UMLa):

Paczka1 ------* Bloczek1 -------* DPDANPB_1
Paczka2 ------* DPDANPB_2

Wszystko jasne i oczywiste, odpalamy Eclipse, tworzymy 5 klasek, adnotujemy @Entity, radośnie klepiemy atrybuty i gotowe.

Za parę miesięcy ktoś będzie się martwić o zmiany bo na przykład: nowa kierowniczka działu 2 nie będzie chciała być gorsza i wprowadzi u siebie również dwustopniowy podział. Następnego dnia kierowniczka działu 1 chcąc pozostać lepszą (od tej ździry z dwójki) wprowadzi trójstopniowy podział. Albo - o zgrozo - będąc w fazie cyklu gdy w jej ustroju dominują męskie hormony wprowadzi 8 stopniowy podział - BO TAK!!!

Możemy oczywiście założyć, że nie zależy nam na przygotowaniu giętkiego systemu otwartego na nowe wymagania, np: atak kosmitów lub co gorsza nierokendrolowe dni kierowniczek. Nie chcemy wydawać kasy na dobry projekt, odwalamy chałę, wdrażamy metodykę RÓB! Takie założenie biznesowe jest w sumie fajne, bo zwalnia mózg developerów od myślenia a co za tym idzie chroni go przed śmiertelnym przegrzaniem - hehe krótkowzroczni biedacy nie widzą, że do czasu...

Jak się jednak okazało (zresztą nie tylko w tym przypadku), że chwila zastanowienia nic nie kosztuje i jest inwestycją pozwalającą zaoszczędzić niemałe pieniądze już w niedalekiej przyszłości.

Wystarczy się nieco wnikliwej przyjrzeć zachowaniu klienta - dokonać rzetelnej analizy. Użytkownicy z działu 1 wiedzeni wrodzonym instynktem niemal intuicyjnie podsuwają nam rozwiązanie: stary dobry model "plików w katalogu".

Uogólniając mamy po prostu jakieś dokumenty pogrupowane w drzewiaste struktury katalogów: katalog zawiera inne katalogi oraz dokumenty:

Katalog -------* Dokument
 |      *
 |----|

Jeszcze 2 kwestie z perspektywy projektanta OO:

W razie potrzeby konkretne druczki (DPDANPB_1, DPDANPB_2,...) mogą dziedziczyć po dokumencie jeżeli chcemy dodać im specyficzną odpowiedzialność biznesową - anemicznemu modelowi mówimy stanowcze NIE. Dziedziczenie jest akurat w tym wypadku usprawiedliwione ponieważ dokumenty będą traktowane polimorficzne na przykład w logice nadzorowania - zatem jesteśmy zgodni z zasadą podstawienia Liskov - możesz używać dziedziczenia tylko wówczas gdy spodziewasz się polimorfizmu.
A co do technicznych aspektów persystencji naszych dokumentów polecam artykuł Dawida Walczaka: JPA - modelowanie dziedziczenia.


Co natomiast ze szczególnymi typami katalogów (węzłów drzewa)? Tu akurat dziedziczenie niezbyt mi się podoba - nie wiem dlaczego.
Zamiast dziedziczenia zalecałbym przede wszystkim podpinanie pod nie strategii.
Czyli nasz katalog mógłby mieć np metodę validate(), która to deleguje proces walidacji do konkretnego walidatora - oczywiście stojącego za interfejsem strategii.

Oczywiście w razie gdy pewien rodzaj katalogu ma specyficzną odpowiedzialność, której nie mają inne to nie możemy pozwolić na "brudzenie" klasy katalogu jakąś ezoteryczną metodą. Drugim (nie wykluczającym rozwiązaniem) pozwalającym na "dodanie" nowych odpowiedzialności do klasy Katalogu jest wzorzec wizytatora. Czyli w tym przypadku katalog miałby metodę pozwolSieZwintestygowac(Visitor v).

//=============================
To był bardzo szczególny przykład - natomiast ogólna idea jest taka: klient zazwyczaj mówi do nas obiektami (instancjami) biznesowymi. Rola analizy jest między innymi taka aby odkryć klasy tych obiektów. W naszym przykładzie klient posługiwał się pojęciami paczka1, paczka2, bloczek. Naiwna analiza przekłada je na klasy. Natomiast w rzeczywistości są to pewne szczególne obiekty klasy Katalog.

W sumie to nie bez powodu w UML istnieje diagram obiektów.


Jeszcze bardziej ogólną analizę problemów analizy przedstawił Michał Bartyzel w swym poście "Metaprogramy w tworzeniu oprogramowania" (wytłuszczenia są moje):

[Klienci] Opisują to jednak w postaci procesów, natomiast analityk widzi system jako strukturę. Podążając za procesem łatwiej dokonywać w nim zmian. Z kolei zmiany wprowadzane w strukturze wymagają przeanalizowania całej koncepcji od nowa. Podczas zbierania wymagań klient często zastanawia się nad pożądanym sposobem pracy. Próbuje różnych alternatywnych przebiegów tego samego procesu i oczekuje od analityka pomocy w wyborze najlepszego rozwiązania. Jeśli analityk nie podąża w procesie za klientem, lecz percypuje system jako strukturę, to ma nie lada kłopot. Alternatywne ścieżki procesu klienta zazwyczaj mają istotny wpływ na domniemaną strukturę systemu i/lub implementację.


Polecam lekturę całego posta - jest arcy ciekawy, bo zanurzony w kontekście Metaprogramów myślenia, o których wspomniałem już 2 krotnie:
- The Unforgiven
- Twoja rola w zespole

Polecam też oczywiście całego bloga Michała Bartyzela - jest zanurzony w różnych innych ciekawych kontekstach.