poniedziałek, 20 kwietnia 2009

Lazy Loading a sprawa wydajności




Podążając za ciągiem skojarzeń: od przedwczorajszego DAO przez wczorajsze Lazy Loading dziś czas na aspekt wydajności podczas korzystania z maperów relacyjno-obiektowych. Właściwie to główne niebezpieczeństwo tkwi w tytułowym Lazy Loadingu.

Sprawa jest w sumie prosta - trzeba uważać co się robi i mniej więcej orientować się jak działa mapper. Nie trzeba wnikać nawet w aspekty implementacyjne Lazy Loadingu: klasy pośredników, które mogą być podstawiane do encji czy to poprzez manipulację byte codem czy w czasie generowania kodu źródłowego (jednak zainteresowanym polecam zgłębienie tematu - warto wiedzieć "jak oni to zrobili").

Niestety na żadnej ze stron domowych maperów zgodnych z JPA (Hibernate, OpenJPA, Kodo, TopLink) nie dopatrzymy się ani na głównej stronie, ani na żadnej innej wielkiego czerwonego napisu "UWAGA NA WYDAJNOŚĆ PODCZAS STOSOWANIA LAZY LOADING!". Nie uraczymy również zestawu paru prostych porad, które uchroniły by nas przed dramatycznym spadkiem wydajności. Czyżby nie wyglądało to zbyt marketingowo? Może autorzy sądzą, że ludzie tego nie zauważą i jakoś to będzie - kolejny bubel się przyjmie w społeczności:)

Problem wydajności jest stary jak Hibernate ale niestety jeszcze nie wszędzie uświadomiony. Jako przykład podam tę oto przypowieść.



Niezbyt_dawno, niezbyt_dawno temu w pewnej dużej organizacji strasznie mulił system wykorzystujący Hibernate. Ludzie ze swą wrodzoną skłonnością do tworzenia mitów szybko poradzili sobie z dysonansem poznawczym trapiącym ich mózgi: stworzyli sobie prosty system mitologiczny, który nakazywał im wierzyć, że ORMy tak po prostu mają. Po prostu ich magiczne właściwości powodują, że systemy spowalniają a odczyt danych z bazy jest hamowany jakąś niepojętą siłą. Ciemnotę dodatkowo potęgowała Kasta Adminów Baz Danych, mówiąc: "tak oto kończy się sprzeniewierzanie prastarej składni SQL. Nie chciało się wam niewierni pisać procedur i kwerend to teraz sczeźnijcie w piekle!".


Po wstępnych oględzinach okazało się, że w większości przypadków obarczonych bardzo niską wydajnością mamy do czynienia z klasycznym problemem występującym w ORMach z Lazy Loadingiem: n+1 Select Problem.

Istotę problemy wyjaśnię na przykładzie: Wyobraźmy sobie, że pobieramy listę obiektów klasy Person. Następnie iterujemy po tej liście i dla każdej osoby wołamy getAddresses() po to aby coś tam z adresami każdej z osób zrobić. Jeżeli zapytanie wygląda mniej więcej tak: SELECT p FROM Person p to mam rzeczony problem. Najpierw silnik persystencji wysyła do bazy jedno zapytanie w celu pobrania listy osób. Następnie dla każdej z n osób wysyła zapytanie o jej adresy. W sumie n+1 zapytań:) Pięknie nieprawdaż? Niestety musimy pofatygować się i napisać porządne zapytanie HQL (lub odpowiednie Criteria w Hibernate), które spowoduje dodżojnowanie adresów do osób tak aby wysłane zostało do bazy jedno zapytanie SQL. Niestety nieodzowne będzie dopisanie w HQL paru JOIN FETCH - a co za tym idzie korzystanie z Entity Managera przestaje być takie trywialne jak w tutorialach, pojawia się złożoność, którą warto hermetyzować w DAO/Repozytorium.

Należy uważać również na "utajony" wariant n+1 select problem. W powyższym przykładzie mamy sytuację, w której radośnie iterujemy sobie przy pomocy własnego kodu po osobach i grzebiemy w ich adresach. Może być też tak, że nasz własny kod jest OK, ale niefortunnie podepniemy naszą listę osób pod np jakiś komponent GUI (np h:dataTable w JSF), który ma wyświetlić osoby wraz z adresami i nieszczęście gotowe. Logika renderingu tabelki przeiteruje po osobach aby wyrenderować jej adresy. Wówczas mogą zdarzyć się 2 rzeczy:
- aplikacja się wywali z powodu LazyInitializastionException (lub jakiegoś innego wyjątku w zależności od dostawcy JPA - co zresztą uważam za skandaliczne) ponieważ najpewniej w warstwie widoku sesja będzie już dawno zamknięta
- jeżeli jednak stosujemy jakiś trik typu Open Session In View to wówczas ORM radośnie wygeneruje nam n+1 zapytań do bazy:)))

Pierwszy wariant (wyjątek) jest o tyle dobry, że przynajmniej zwróci czyjąś uwagę podczas devlopmentu i skłoni do napisania porządnego zapytania, które ładuje wszystkie dane w jednym podejściu. Drugi wariant działa więc jest szansa, że nitk nie sprawdzi co się dzieje po stronie bazy:/



Kolejna przypowieść. W tejże samej organizacji istniał inny system, który również niemiłosiernie mulił. Tym razem był to gruby klient do serwera więc lazy loading nie miał zastosowania. Encje przesłane na klienta nijak nie chcą nawiązać kwantowego kanału z procesorem serwera:/ W tym systemie również panowała mitologia. Kiedyś pewien magik podobno zrobił czary mary i LazyInitalizationException nie pojawiał się gdy na kliencie wołano łańcuszki get get get.


Po krótkiej analizie problemu okazało się, że tym razem przedobrzono w inną stronę. Prawie wszystkie powiązania pomiędzy obiektami były EAGER - czyli chciwe lub jak kto woli łapczywe. Skutkowało wyciąganiem połowy bazy przy każdym zapytaniu. O ile serwer bazodanowy dawał radę to wąskim gardłem okazała się transmisja zserializowanych danych. Wniosek znowu ten sam: niestety na skróty się nie da - trzeba się pofatygować i napisać porządne zapytania specyfikując co w jakim Use Case ma być podciągnięte. Niestety zakorzeniona mitologia i szargana opinia o ORMach pozostanie w organizacji na parę następnych pokoleń.

Przy okazji warto wspomnieć o kolejnej pułapce jaką szykuje na nas maper. Uwaga na powiązania typu n-1 lub 1-1 - czyli takie gdzie w klasie encji mamy pole klasy innej encji (np Person ma jeden Adres). Standard JPA zakłada, że wówczas powiązanie jest typu EAGER. Czyli pobierając z bazy osobę zawsze od razu wyciągniemy jej adres. Niestety odbywa się to zazwyczaj w osobnym zapytaniu. Czyli w przypadku pobierania listy osób mamy n+1 Select Problem:)


Żeby nie było, Lazy Loading może również przydać się w celu optymalizacji systemu.
Wyobraźmy sobie taki przypadek: Mamy klasę dokument, która zawiera szereg pół - w tym pole zawierając treść dokumentu: kilkadziesiąt stron A4 (pomińmy kwestię sensowności takiego projektu). Gdy chcemy zaprezentować listę dokumentów na GUI, która zawiera dajmy na to tytuł i datę to oczywiście zupełnie niepotrzebnie będziemy pobierać z bazy i przechowywać w aplikacji (choćby chwilowo) dziesiątki megabajtów tekstu. Jednym z paru rozwiązań ale najprostszym jest ustawienie leniwego ładowania kolumny z treścią. Gdy nie będzie potrzebne wówczas nie będzie ładowane z bazy.


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

Podstawą pracy z ORM jest konsola z logami. Podczas developmentu należy włączyć wyświetlanie generowanych poleceń SQL i nieustannie obserwować oczami swymi logi silnika ORM czy przypadkiem nie pojawia się na niej w pewnym momencie kilka/-dziesiąt/-set/-tysięcy zapytań. Oczywiście n+1 Select Problem możemy zauważyć na konsoli praktycznie tylko wówczas gdy napełnimy bazę większą ilością danych. Dla n=1 problemu raczej nie uraczymy.

W dobrym procesie produkcji softu jest czas na takie coś jak kontrola kodu - przeglądanie nowego kodu przez bardziej doświadczonych członków teamu (lub chociaż przez drugą parę oczu). Co powiecie na kontrolę konsoli? Pokaż misiu jakie zapytania do bazy generuje Twój kod.

Podsumowując dwie przytoczone przypowieści pozwolę sobie zacytować Franka Zappa: "Głupota ma pewien urok, ignorancja nie".

niedziela, 19 kwietnia 2009

DAO a sprawa Lazy Loading



Wracając do wczorajszego tematu DAO rozwinę nieco kontekst mapowania relacyjno-obiektowego na przykładzie standardu JPA. Dokładnie - skupię się na głównym ficzerze każdego mapera zgodnego z JPA, czyli tytułowym leniwym (opóźnionym) ładowaniem zagregowanych w encji obiektów.

Niektórzy pewnie zastanawiają się czego znowu ten zgred się czepia. Przecież LL to taki fajny ficzer. Mamy sobie załadowany z bazy obiekt (i jeżeli sesja kontekstu perzystencji nie została zamknięta) to wystarczy zawołać na nim getXXX(), czary mary i pobrany przez getter zagregowany obiekt został podciągnięty z bazy oraz zapakowany do encji.

Wszystko fajnie, ale jeżeli korzysta się z tego z świadomie i w odpowiednim momencie. W przeciwnym wypadku mamy spore problemy z wydajnością. Ale o tym za w następnym poście. Ja mam z tym jeszcze pewien problem natury filozoficznej, który pojawia się gdy do naszej architektury wprowadzimy warstwę DAO (bez której jak bez ręki - co chciałem wczoraj wykazać)...

Zatem weźmy przykładowy interfejs DAO:


public interface PersonDAO{
Person getPerson(Long id);
}


Standard... zwykłe DAO (nie czas teraz na Repozytorium z DDD), zwykły klucz (nie czas teraz na UID czy klasę kluczą w dobrym stylu DDD).

Dalej będzie oczywiście jedna z możliwych implementacji w JPA:


public class JpaPersonDAO implements PersonDAO{
EntityManager em;
Person getPerson(Long id){
return (Person)em.find(Person.class, id);
}
...
}


Też standard, bez żadnego generic DAO czy innych klas bazowych, bez wnikania w szczegóły wstrzykiwania EM. Nie w tym rzecz, chodzi tak jak wspomniałem o problem natury filozoficznej, mianowicie:
Co ja mam napisać w komentarzu metody getPerson() w interfejsie? (i od razy mówię, że banał typu "return returns person that has ID relevant do given id" mnie nie zadowala)

Aby w komentarzu napisać prawdę, całą prawdę i tylko prawdę musiałbym popełnić coś takiego (po polsku będę komentarz pisał - wybaczcie):

"
Metoda zwraca osobę o zadanym ID. Drogi programisto-użytkowniku klas implmentujących ten interfejs, wiedz ,że niniejszy interfejs zawirea z tobą kontrakt o następujących warunkach:
1. O ile korzystasz z implementacji DAO opartej o JPA to możesz się spodziewać tego, że:

1.a Niektóre klasy obiektów zagregowanych w zwróconej instancji osoby zostaną zastąpione przez klasy specyficznych pośredników (np PersistentBag w hibernate) więc niniejszy interfejs nie może zagwarantować Ci poprawnej pracy w środowisku rozproszonym (np aby wynik działa tej metody przesłać na zdalnego klienta musisz mu zapewnić odpowiednie biblioteki w odpowiednich wersjach).

1.b Być może masz dostęp do obiektów zagregowanych w zwróconej instancji. Na przykład do adresu (przez metodę getAddress). Zależy to od tego czy znajdujesz się w miejscu, w którym jeszcze obowiązuje sesja kontekstu persystencji. Jeżeli sesja będzie zamknięta, a ty będziesz brzydko się bawił z instancją person to dostaniesz np LazyInitializationException - ale tego w tym momencie nie mogę zagwarantować, gdyż zależy to od dostawcy (specyfiakcja JPA nic nie narzuca w temacie reagowania na zamkniętą sesję). Niestety niniejszy interfejs jest czystą abstrakcją dostępu do danych i nie mogę Ci zdradzić szczegółu na jakim dostawcy JAP będzie oparta implementacja tego interfejsu.

2. Jeżeli implementacja tego interfejsu nie będzie oparta o JPA (a np o dostęp do web service albo pliku XML) to wiedz, że dostępu do żadnych zagregowanych obiektów nie masz.

return osoba o zadanym ID, z którą ni cholery nie wiadomo co można na 100% bezpiecznie zrobić.
"


Ogólnie chodzi mi o to, że staroświeckie DAO i nowoczesny mechanizm Lazy Loadingu mają się nijak do siebie.

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

Mamy tu do czynienia z bardziej ogólnym problemem: przeciekającą abstrakcją. Szczegóły techniczne mappera relacyjno-obiektowego (takie jak lazy loading, problem z wygasłą sesją) literalnie wyciekają z interfejsu DAO, który powinien być czystą abstrakcją dostępu do danych niezależną od jakiś maperów. Innymi słowy klient (kod) interfejsu DAO musi być niestety świadomy pewnych "wyciekających" mechanizmów stojących za jedną z jego implementacji. Nie może jej traktować jako czarnej skrzynki, która robi to co ma robić - i robi to dobrze.

sobota, 18 kwietnia 2009

DAO



Jak donoszą brukowce DAO odeszło i zostało zastąpione przez JPA. Czy aby na pewno?

Piotrek w swym poście (będącym niezłym zbiorem argumentów w dyskusji) wyraził zdziwienie pojawiającymi się od pewnego czasu głosami przeciwko DAO. Natomiast ja chciałbym dodać od siebie dodatkowo nieco typowej dla siebie irytacji spowodowanej wąskim spojrzeniem na problem pojawiającym się w głosach autorytetów z wielkiego świata.

Problem potrzeby lub nie warstwy DAO nie jest oczywisty i zależy od kontekstu. Czasem DAO jest niezastąpione a czasem może lekko przeszkadzać. Ale na pewno pozbawione kontekstu autorytarne głosy typu "DAO bad" są bez sensu.


DAO ma na celu separację logiki dostępu do danych od logiki biznesowej/aplikacji.
Co nam to daje?
Szeroką i sensowną listę podał Mario Gleichmann w swoim poście.

Ja ograniczę się do paru z nich:

  • wynikającą z separacji możliwość podmiany sposobu dostępu do danych
  • wynikający z możliwości podmiany zwiększony poziom testability
  • reużywalność logiki dostępu do danych


Przyjrzyjmy się zasadności wylistowanych argumentów:
Podmiana sposobu dostępu do danych - teoretycznie silny argument; praktycznie bezużyteczny. Jak często zdarzyła się Wam taka sytuacja? O ile bardzo często zmienią się (no w zależności od klienta) wariacje logiki biznesowej to zmiana źródła danych? O ile zmienną logikę biznesową warto hermetyzować pod stabilnym interfejsem i jej wariacje enkapsulować we wzorcu strategii to robienie tego z dostępem do danych wydaje się nakładaniem gaci przez głowię. Chyba, że...

Testowanie - separacja dostępu do danych od logiki biznesowej zwiększa dramatycznie poziom testability, czyli podatność kodu na testy. Tak podatność na testy - ponieważ nie każdy kod można łatwo testować.
Jeżeli mam osobną klasę realizującą jakąś funkcjonalność biznesową oraz osobną klasę DAO odczytującą/modyfikującą dane to wówczas:
- możemy poddać testom sam "komponent" DAO - co się oczywiście będzie rzadko zdarzać, więc ten argument się nie liczy;P
- możemy testować samą logikę biznesową - na czas testów nasza logika może używać innej implementacji DAO (np takiej, która nie korzysta z bazy danych). Co na to daje?
Testy logiki biznesowej są szybsze ponieważ nie tracimy czasu na operacje zapisu odczytu danych (których poprawność w tym momencie nas nie interesuje). Czas zapisu/odczytu potencjalnie może być stosunkowo długi, wystarczy wyobrazić sobie źródło danych, które jest bazą zawierającą miliony rekordów, gigantycznym plikiem, czujnikiem chemicznym, web servisem wykonującym się średnio 5 min. Zamiast 100000 testów na godzinę, możemy ich wykonać tylko 10 gdy będziemy ciągnąć za sobą bagaż dostępu do danych.

Reużywalność - posługiwanie się wprost EntityManagerem w kodzie biznesowym do pobierania encji (tak jak chcą tego piewcy śmierci DAO) jest pomysłem arcy-chromym.
Kod w stylu
em.find(Person.class, id)
nie wygląda groźnie - nieprawdaż? Niestety jest on jednak typowy dla tandetnych turoriali na poziomie Hello World. W rzeczywistości jednak piszemy kwerendę ponieważ:
- mamy dynamiczne zapytanie (np doklejamy WHERE person.name = 'xxx' o ile user wpisał imie, itp)
- chcemy dociągnąć chciwie zagregowane obiekty (i nie życzymy sobie lazy loadingu gdyż nierzadko JEST ZŁY - o czym będzie w następnym poście)

Nawet jeżeli w pierwszej iteracji nasze pobranie danych jest prostym find() to zwykle za miesiąc zmieni się w kwerendę. Nie chcemy zatem programować metodą Kopiego-Pasty. Dzięki istnieniu DAO możemy reużywać złożoną logikę zapytań oraz oczywiście zmieniać je w 1 miejscu.

Separacja dla mnie osobiście separacja kodu - choćby zapytania były trywialne albo totalnie niereużywalne jest wystarczającą zaletą przemawiającą za DAO.


Potrafię sobie wyobrazić, że żadne z powyższych uwarunkowań nie występują. Tzn:
- nigdy nie zmieniamy źródła danych - co jest naturalne
- nie testujemy kodu - nie oszukujmy się, taka jest rzeczywistość
- nie mamy złożonych zapytań i logiki sklejania HQLa

Wówczas możemy radośnie używać EntityManagera w kodzie biznesowym. Sugerowałbym jednak kompromis: wystarczą osobne klasy realizujące logikę dostępu do danych - bez abstrakcji: bez interfejsu, bez wstrzykiwania.



Wąski kontekst
W bardzo prostych aplikacjach trzymanie się ściśle architektury z warstwą DAO niesie ze sobą pewnie narzuty i utratę prostoty. Mam tu na myśli niezbyt ambitne ale bardzo potrzebne na rynku i popularne aplikacje typu przeglądarka bazy danych. Po prostu łatwiej i taniej jest wyprodukować za 1 mln zł system, który w przeglądarce wyświetli tabelkę z listą kalesonów w hurtowni niż postawić samą bazę danych i przyuczyć pracowników z podstaw SQL;)))

W tego typu systemach zwykle corowa funkcjonalność to ekrany z różnego rodzaju listami + kontrolki do wpisywanie kryteriów + button SZUKAJ. Do tego ekran podglądu i edycji.

Najbardziej produktywne jest wówczas podejście minimalistyczne. Warstwa prezentacji tworzy obiekt Criteria i przesyła go niżej do wykonania. Przekombinowane podejście polegające na tworzenie warstwy DAO, która abstrahuje źródło danych zmusza nas również do abstrahowania od kryteriów wyszukiwania. GUI musi wysyłać jakieś DTO, które w DAO jest przepakowywane w Criteria. Generalnie: nakładanie gaci przez głowę.

Szerszy kontekst
Dla kontrastu rozważmy hipotetyczny przykład skomplikowanego dostępu do danych.
Wyobraźmy sobie, że w naszym systemie mamy użytkownika przechowywanego w 1 bazie danych. Z niewiadomych przyczyn jego zamówienia są przechowywane w innej bazie. Adresy tegoż użytkownika są pobierane z zewnętrznego systemu przez jakiś WebService.
Załóżmy też, że w zależności od wdrożenia druga baza danych (ta z zamówieniami) oraz źródło adresów są zmienne.

Podążając za DDD stworzylibyśmy agregat User oraz repozytorium UserRepository, które jest w stanie go zebrać i poskładać do kupy. Repozytorium mogłoby używać EntityManagera do pobrania encji User oraz dwóch DAO do pobrania zamówień oraz adresów z abstrakcyjnych źródeł. Problem dostępu do danych może zawierać w sobie również aspekt przepakowania obiektów z jednej domeny (np z innego systemu) do domeny innej.


Przykład powyższy może wydawać się nieco wydumany i przesadzony, ale w rzeczywistości tego typu spawy istnieją i nie należy o tym zapominać. Chciałem tylko pokazać, że architektura dostępu do danych wcale nie jest trywialnym problemem jak przedstawia się to w niektórych materiałach lub (o zgrozo!) przez niektórych członków grup eksperckich JEE. Owszem często taki jest. Ale spektrum problemów jest szerokie i po specyfikacji platformy korporacyjnej spodziewalibyśmy się czegoś więcej.

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

Na zakończenie pamiętajmy aby dobierać sobie młotek do problemu, a architekturę dostępu do danych do... też do problemu:)

Sentencja Alberta Einsteina "Wszystko powinno być tak proste, jak to tylko możliwe, ale nie prostsze" może wydawać się trywialna, ale często o niej zapominamy.

A do implementacji większości systemów i tak nie potrzeba żadnego JPA - wystarczy Excel + jakieś cwane makro... no może Access ostatecznie;P

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:)