niedziela, 20 grudnia 2009

Java EE

Kilka dni temu ukazała się finalna wersja specyfikacji Java EE 6.
Jako, że żaden szanujący się blog Javowy nie może nie wspomnieć o tym doniosłym wydarzeniu, zatem niniejszym to czynię:
Proszę Państwa, mamy nową specyfikację Java EE!

Ok, formalności mamy za sobą. Teraz aby ten post jednak coś wnosił do sprawy i aby zachować niezależny charakter bloga pozwolę sobie polecić 2 linki stanowiące niejako rozliczenie dotychczasowych wersji platformy:

- Rod Johnson (twórca Spring framework) w prezentacji Lessons Learned From Java EE’s Evolution opowiada historię platformy, m. in. wpływ niesławnych twórców CORBY na jej kształt:) Człowiek, który zna na wyrywki całą specyfikację Java EE twierdzi, że jedyną dobrą rzeczą jest Servlet API.

- Misko Hevery (Agile Coach w Google odpowiedzialny za automatyzację testów) w swoim poście How to do Everything Wrong with Servlets twierdzi natomiast, że Servlet API jest przykładem bardzo złego designu, bardzo złego.

Aż strach pomyśleć do jakich wniosków doszliby obaj panowie dokonując koniunkcji zbiorów swoich poglądów.

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

Warto zwrócić uwagę na specyfikację JSF 2.0 (wchodzącą w skład Java EE 6), która wreszcie po latach będzie nieco bardziej pomocna do tworzenia aplikacji webowych. Przykładowo otrzymujemy oficjalne wsparcie dla szablonów (bez Feceletów jak bez ręki) oraz lepszą orientację na URL (taki mały szczegół webowy).

niedziela, 29 listopada 2009

Seam == JSF + EJB ?

WSTĘP

Framework Seam opiera się na bardzo silnym mechanizmie - bijekcji.

Bijekcja to nowa jakość w technice Dependency Injection, która pozwala na budowanie bardziej naturalnych konstrukcji Inversion on Control.

/**
 * Na marginesie dodam, że DI i IoC to nie jest to samo.
 * Wyjaśnienie różnic w tym przydługim poście:
 * Wprowadzenie do wstrzykiwania zależności i Springa zarazem
 */

Bijekcja w skrócie przedstawia się następująco:
- podczas wywołania metody
- następuje wstrzyknięcie zależności do obiektu, którego metoda jest wołana (pola adnotowane @In)
- metoda jest wykonana
- następuje wystrzyknięcie obiektów do kontekstu Seam (pola adnotowane @Out)
- następuje czyszczenie (nulllowanie) wstrzykniętych zależności

Od strony technicznej odbywa się to w Seam dzięki specjalnym interceptorom. Przechwytują one wywołania metod wszystkich komponentów i wokół tych wywołań dodają całą opisaną powyżej magię.

Koncepcja jest jak już napisałem piękna, ponieważ pozwala na budowanie dużo bardziej naturalnych konstrukcji. Wstrzyknięcia następują per wywołanie metody a nie jedynie raz, po stworzeniu komponentu - zaspawane na wieki wieków (albo przynajmniej do restartu servera z powodu zwisu aplikacji;)
Dodatkowo niemal bez ograniczeń możemy wstrzykiwać w siebie komponenty o różnych zasięgach - nie ma ograniczenia szerszy zasięg w węższy. Dlatego, że wstrzyknięcie następuje jedynie na czas wywołania metody i nie będziemy mieli nigdy problemu z nieświeżą instancją komponentu.


PROBLEM

Twórcy frameworka poszli jeszcze dalej...
Umożliwiają używanie Sesyjnych EJB wprost w JSF - bez potrzeby pośredniej warstwy Managed Beanów (lub ich specyficznej odmiany Backing Beanów). Czyli komponenty Seam, które są jednocześnie komponentami EJB będą widoczne w kontekście JSF.
Managed Beany są podobno zbędą warstwą, która tylko przeszkadza. Możliwość wołania EJB z JSF jest rzekomo tryumfem rozumu na platformą korporacyjną.

Hmmm w bardzo prostych systemach klasy "Przeglądarka Bazy Danych" rzeczywiście można by się obyć bez MB, ale w niniejszym poście przedstawię do jakich kuriozalnych konstruktów dochodzi gdy w nietrywialnych przypadkach wiążemy JSF wprost z EJB.

Na wstępie zaznaczam, że na potrzeby niniejszych rozważań pomijamy aspekty warstwowej architektury, elementarnych zasad projektowania mówiących o kohezji klasy i temu podobnych staromodnych ograniczeniach. Skupiamy się na "produktywnym" kodowaniu na wyścigi rodem z najlepszych tutoriali i książek.

Zaczynamy.
Przykład prosty, klasyczny ekran prezentujący listę czegoś.
Wymagania:
Po ordynarnym wejściu na stronę przez GET chcemy aby lista wyświetlała wszystkie dane.
Widok ma pozwalać również na wyszukanie czegoś po jakimś atrybucie - czyli wpisanie szukanego słowa w pole tekstowe i naciśnięcie buttonu z zaokrąglonymi rogami "Szukaj" (POST gwoli ścisłości).




Na początek implementujemy pierwsze wymaganie: po wejściu na stronę widzimy listę wszystkiego...

Strzępek kodu widoku:

<h:dataTable value="#{listaCzegos}" var="_cos">
<h:column>#{_cos.nazwa}</h:column>
...
</h:dataTable>


Stateless Session Bean, który dostarcza danych dla widoku:


@Stateless
@Name("cosProwajder")
public class CosProwajderBean implements CosProwajderLocal{
@Out
private List listaCzegos;

@Factory("listaCzegos")
public void initListaCzegos(){
listaCzegos = //pobranie danych
}
}


Co się tutaj dzieje: JSF rząda komponentu listaCzegos. Seam widzi, że nie istnieje on w kontekście, ale na szczęście znalazł ochotnika, który go sfabrykuje - metodę otagowaną adnotacją @Factory("listaCzegos"). Metoda zostaje wywołana, metoda ustawia pole prywatne, a ponieważ pole jest adnotowane @Out to po chwili jest wystrzykiwane do kontekstu. Dzięki temu JSF "widzi" listę i może ją już teraz spokojnie renderować w tabelce.

Kod Session Beana mógłby równie dobrze wyglądać tak:

@Stateless
@Name("cosProwajder")
public class CosProwajderBean implements CosProwajderLocal{

@Factory("listaCzegos")
public List initListaCzegos(){
listaCzegos = //pobranie danych
return listaCzegos;
}
}


Ale już śpieszę wyjaśnić skąd poprzednia konstrukcja. Mam już w zamyśle spełnienie drugie wymagania - funkcjonalności wyszukiwania. Zatem na widoku pojawi się pole tekstowe i button:


<h:form>
<h:inputText value="#{searchFilter.name}" />
<h:commandButton action="#{cosProwajder.search}" />
<h:form>


Nasz Session Bean dostanie metodę search, która wyszuka dane na podstawie wstrzykniętych kryteriów, następnie wynik ustawi w prywatnym polu, z którego to po chwili wartość zostanie wystrzyknięta do kontekstu Seam, skąd JSF będzie ją widział.


@Stateless
@Name("cosProwajder")
public class CosProwajderBean implements CosProwajderLocal{
@Out
private List listaCzegos;

@In
private SearchCriteria searchCriteria;

@Factory("listaCzegos")
public void initListaCzegos(){
listaCzegos = //pobranie danych
}

public void search(){
listaCzegos = //pobranie danych na podstawie searchCriteria
}
}


Jeszcze tylko dla ścisłości komponent przechowujący kryteria wyszukiwania. Zasięg PAGE aby kryteria były widoczne po przeładowaniu strony:

@Name("searchCriteria")
@Scope(PAGE)
public class SearchCriteria implements Serializable{
private String name;
//getter i setter
}



Pytanie: co jest nie tak z poniższym kodem?

@Stateless
@Name("cosProwajder")
public class CosProwajderBean implements CosProwajderLocal{
@Out
private List listaCzegos;

@In
private SearchCriteria searchCriteria;

@Factory("listaCzegos")
public void initListaCzegos(){
listaCzegos = //pobranie danych
}

public void search(){
listaCzegos = //pobranie danych na podstawie searchCriteria
}
}


Dla ułatwienia wyrzucę linijki odwracające uwagę i zaznaczę kluczowy element:
(To przecież nie jest egzamin na certyfikat - chcemy się tu dowiedzieć czegoś pożytecznego)


@Stateless //<<-------------
public class CosProwajderBean implements CosProwajderLocal{
private List listaCzegos;

private SearchCriteria searchCriteria;

public void search(){
listaCzegos = //pobranie danych na podstawie searchCriteria
}
}


Właśnie!
Niby mamy bezstanowy komponent, ale korzystamy z niego w stanowy sposób!

Wyobraźmy sobie, że nasz wspaniały komponent biznesowy jest tak genialny, że chcemy go wykorzystać jeszcze gdzieś poza JSF, np wywołać zdalnie. Musimy wówczas:
1. ustawić kryteria wyszukiwania (zakładając, że mamy setter)
2. odpalić metodę search(), która zmieni stan - tu jest ta nieszczęsna stanowość
3. odebrać wynik przez getter

Czyli JSF wymusza na nas styl "strzelania z muszkietu": załaduj i wypal.

Czy dałoby się wykorzystać mimo wszystko ten super-kod poza Seam, np przykrywając go fasadą:


@Stateless
public class CosProwajderFacadeBean implements CosProwajderFacadeRemote{
@Ejb
private CosProwajderLocal cosProwajder;
public List search(SearchCriteria searchCriteria){
cosProwajder.setSearchCriteria(searchCriteria);
cosProwajder.search();
return cosProwajder.getListaCzegos();
}
}

Niestety NIE, ponieważ nie mamy gwarancji, że kontener JEE przy każdym z 3 wywołań bezstanowego komponentu zaserwuje nam tą samą instancję!


Dlaczego ten bezsensowny kod działa w ogóle w Seam? Tak jak napisałem na wstępie - interceptory Seam. Jeden z nich przechwytuje wołanie metody search na komponencie, wstrzykuje kryteria, wykonuje metodę, wystrzykuje wynik. Ponieważ wstrzykiwanie i wystrzykiwanie nie są wywołaniem metod biznesowych bezstanowego komponentu to interceptor cały czas operuje na tej samej instancji.


ROZWIĄZANIE

Łatwo można rozwiązać problem sensowności kodu zmieniając bean bezstanowy na stanowy. Jednak wciąż mamy problem z bezsensownością logiczną. Dlaczego jakiś komponent będący de facto wrapperem dla procedury ma być stanowy?

Owszem w pewnych sytuacjach stanowość może mieć sens, np: z przyczyn wydajnościowych komponent stanowy trzyma wynik jako jakiś kursor po stronie bazy. Klient Stanowego Komponentu Sesyjnego przegląda listę wynikową po kawałku. Tę argumentację zaliczam.

Innym usprawiedliwieniem może być chęć wybrania (kliknięcia) wiersza - z technicznych powodów musi wówczas przechować listę. Innym jeszcze usprawiedliwieniem może być naiwna paginacja, która w naiwnych paginatorach działa na danych sesyjnych pobranych w całości z bazy.
Czy jednak sensowne jest dopasowywanie API komponentów biznesowych do takich szczegółów technicznych jakiś frameworków prezentacji?
Poza tym wciąż będziemy mieli kuriozalne korzystanie z niego w fasadzie - w stylu "strzelania z muszkietu": załaduj i wypal.


PRAWDZIWE ROZWIĄZANIE

Aby nasze komponenty biznesowe miały sensowny interfejs w nietrywialnych przypadkach musimy ponieść ten niesamowity trud wprowadzenia warstwy jakiś Managed Beanów - w Seam zwanych Akcjami.

Są to zwykłe POJOs, które mają API w stylu "strzelania z muszkietu" a logikę biznesową delegują do EJB:


@Name("cosControler")
public class CosControler{
@In //EJB
private CosProwajderLocal cosProwajder;

@Out
private List listaCzegos;

@In
private SearchCriteria searchCriteria;

@Factory("listaCzegos")
public void initListaCzegos(){
//używamy pustych kryteriów (w celu optymalizacji można by wynieść je do singeltona)
listaCzegos = cosProwajder.search(new SearchCriteria());
}

public void search(){
//Wywolanie EJB
listaCzegos = cosProwajder.search(searchCriteria);
}
}


Jest to również doskonałe miejsce do wstrzyknięcia np kontekstu FacesMessages, kontekstów Seam, parametrów Request, bindowanie UIComponent i innych zależności typowych dla technikaliów frameworków. W tej warstwie możemy sobie na to śmiało pozwolić i dzięki niej nie musimy brudzić EJB zależnościami od Seam i JSF.

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

Sama możliwość wołania Sesyjnych EJB z JSF jest oczywiście bardzo wygodnym ficzerem i warto czasem z niej korzystać. Ale tylko wówczas gdy ma to sens i jest racjonalnie uzasadnione.

Tworzenie komponentu biznesowego, który jest "zbrukany" stylem i zależnościami pewnych technologii powoduje, że nie jest to już ani komponent ani biznesowy. Komponent - czyli pewna reużywalna część; biznesowy - czyli zajmujący się jedynie logiką biznesową.

środa, 25 listopada 2009

Geneza i ograniczenia OO

Dziś jedynie dwa linki, które uważam za godne polecenia.

Przeczytałem niedawno tekst, który lekko zachwiał moim światopoglądem: Aristotle's Error or Agile Smagile. Tekst traktuje ogólnie o historii - znajdziecie tam również rzekomą genezę Object Oriented.

Rzekomo pierwotnie wcale nie chodziło o modelowanie świata, lecz o zabawy ze stertą - co będzie gdy zamiast chwilowo przechowywać zmienne na stosie wrzucimy je na dłużej do sterty.

Teorię OO Analysis i OO Design rzekomo "dorobiono" później. Tak, tak, wiem - sam jestem w szoku podobnym do tego gdy wydedukowałem, że nie ma żadnego św Mikołaja;P

Nie wiem na ile historia o pierwocinach OO jest prawdziwa, ale tym razem wielki szacunek dla Uncle Boba za zdystansowane podejście.



Kolejny link to ciekawa prezentacja człowieka, któremu doświadczenie również pozwala zdystansować się od mainstreamu Are We There Yet?. Prezentacja pozwala uświadomić sobie ograniczenia OO oraz wyjaśnia (tym, którzy jeszcze tego nie widzą) dlaczego niektóre wydawałoby się proste języki wprowadzają wysoką złożoność (przypadkową).

Zobaczycie też jak bardzo podobne są do siebie języki, o które często spieramy się "który lepszy". Nie brakuje też aluzji do takich "przełomowych usprawnień" jak usunięcie średników:) Dowiecie się również z czego wg autora prezentacji wynika potrzeba posiadania testów:)

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

Od razu ostrzegam, że treści kryjące się pod linkami są mocno filozoficzne, ale może warto oderwać się na chwilę od kodu aby później móc spojrzeć na niego inaczej...

poniedziałek, 9 listopada 2009

Czysty kod to przetestowany kod?

Clean Code to skądinąd szczytna idea, na fali której kilka osób chce się wylansować ostatnimi czasy. Przyjrzymy się jej dzisiaj nieco bliżej aby sprawdzić czy nie kryje się za nią przypadkiem merytoryczna próżnia...

Ewangelizatorem tej idei jest nasz ulubiony Wujek Dobra Rada - Uncle Bob. Polecam wszystkim jego prezentację pt Craftsmanship and Ethics.

Uncle B z pasją opowiada o profesji i etyce w naszej branży. Właściwie to przekonuje, że już najwyższy czas aby wykrystalizowały się one bo póki co jeszcze się ich nie dorobiliśmy. Tutaj zgadzam się z nim bez 2 zdań. Metodyka byle-jak-byle-działało-ale-miało-zaokrąglone-rogi-na-gui to aktualnie standard.

Jako że są to idee bardzo mi bliskie to pierwsza część prezentacji jest na prawdę budująca. Dalej Uncje B twierdzi, że drogą do profesjonalizacji i nieskazitelnego kodu jest Test Driven Development. "Nie wolno Ci napisać ani linijki produkcyjnego kodu bez napisania uprzednio testu!" grzmi wujaszek! Kropka.

Postępuj tak a w nagrodzę po śmierci będą czekać na Ciebie w niebie zastępy dziewic - do tego nimfomanek rozumiejących maszynowy kod w reprezentacji binarnej. Marzenia...



...ale chyba nie będzie tak łatwo.

TDD owszem jest techniką, która ma swoje przeznaczenie i zastosowania ale nie dajmy się zwieść, że rozwiązuje wszystkie problemy. Co najwyżej niektóre.

Sensowna krytyka tego typu rozumowania znajduje się w typ poście: It's OK Not to Write Unit Tests. Wujkowi Dobra Rada chyba tylko wydaje się, że rozprawił się z adwersarzem przy pomocy racjonalnych argumentów: Excuse me sir, What Planet is this?

O ile w pewnych sytuacjach testy mogą "sterować" developmentem to nie zapominajmy o technikach, które rzeczywiście prowadzą do clean code.

Co nam po nawet 100% pokryciu kodu testami jeżeli kod będzie jedną wielką kupą...
Działającą nawet perfekcyjnie, ale wciąż jedną wielką kupą... [wstawić_swoją_ulubioną_metaforę]

Refaktoryzacja to podstawa? Co nam po wsparciu dla refaktoryzacji dzięki pokryciu kodu testami, które zwalniają nas z napięcia towarzyszącemu zmianom skoro... nie mamy pojęcia jak refaktoryzować ani w ogóle, w którą stronę refaktoryzować?

Do tego właśnie zmierzam... TDD nie daje żadnych wskazówek jakich technik developerskich użyć aby osiągnąć upragniony Clean Code. To oczywiste, że nie daje wskazówek, bo nie leży to w "domenie" TDD. Ale trzeba to sobie uświadomić i nie dajmy sobie wcisnąć kitu, że testy jednostkowe rozwiązują problem - są po prostu niewystarczające. Pomagają rozwiązać - ale rozwiązanie właściwe leży chyba gdzie indziej.

Czysty kod, aby powstał musi być wypracowany poprzez pewien wysiłek umysłowy. Nie wystarczy dorwać się do klawiatury i radośnie stukać (o tym na koniec). Na ten proces musi składać się rzetelna analiza problemu, projekt i dopiero implementacja. Nie mam tu oczywiście na myśli metodyki Waterfall, ale po prostu pewnej pracy, którą trzeba wykonać - nawet w umyśle jednej osoby.

Dla każdego z tych etapów mamy zestaw sprawdzonych technik oraz narzędzi, które oczywiście nie gwarantują sukcesu, ale zwiększają prawdopodobieństwo powstania upragnionego Clean Code.

Na poziomie analizy możemy posłużyć się Archetypami Modeli Biznesowych. Są to skatalogowane gotowe rozwiązania standardowych problemów biznesowych. Modele te zostały wypracowane przez wytrawnych analityków dzięki czemu można je dostosowywać do swych potrzeb. Są również zaprojektowane z myślą o rozbudowie modelu. Można by dużo o nich pisać, ale nic nie zastąpi przykładu. A przykład w jednym z najbliższych postów. Póki co zastanówmy się co nam po doskonale obłożonym testami modelu, który jest kupą...

Gdzieś na styku analizy i projektu mamy na przykład Domain Driven Design.

Zanim przejdziemy do poziomu projektu zatrzymajmy się przez chwilę na architekturze, będącej stylem, na podstawie którego będziemy projektować. Zastanówmy się co nam po doskonale obłożonym testami kod jeżeli architektura jest kupą...

Na poziomie projektu mamy zestaw technik np Object Oriented. GRASP i SOLID - pomagające w poprawnym interpretowaniu OO i uniknięciu proceduralnego kodu dziwacznie upchanego w klasy.
Nie zapominajmy o Wzorcach Projektowych. Kiedyś było o nich głośno, ale aktualnie młodzież woli sobie wyklikać/wygenerować trochę kodu. Ci bardziej Agile obłożą go paroma testami i jest git;)
Wzorce mogą pomagać u utrzymaniu czystej struktury kodu, ale przy ich "pomocy" można również bardzo skutecznie niepotrzebnie zagmatwać powinny pozostać prostsze - cała sztuka w wyważeniu. Ale tego oczywiście nie dowiemy się z marketingu TDD;)

Co do wzorców to planuję kiedyś stworzyć serię postów z realistycznymi (nieksiążkowymi) przykładami - ale jak zwykle czasu brak. Co do GRASP i SOLID to można znaleźć trochę dobrych przykładów - np ten.

Kiedy już wiemy co i jak to na poziomie implementacji możemy podeprzeć się techniką testów jednostkowych. Ale to czy kod będzie Clean zależy od pracy umysłowej włożonej również (albo przede wszystkim) wcześniej.

Konkludując: TDD ew. jako dodatek na drodze do Clen Code wujaszku;P


Aby uzupełnić całość warto wspomnieć o podejściu Specify First - dobry opis w poście TDD is not test-first. TDD is specify-first and test-last. Niektórzy twierdzą, że dopiero przy takim podejściu można mówić o prawdziwym TDD.
Chodzi generalnie o to, że pisząc najpierw test - nawet nie interfejs implementacji, a test - stawiamy się w roli kodu "klineta" danej klasy. Na pewno wpływa to pozytywnie na nasz design - bardzo pozytywnie.

Ale to znowu półśrodek. Technika owszem dobra, ale dla tych z nas, którzy są wciąż jeszcze na poziomie myślenia kodem.

Myślenie kodem to zupełnie normalna faza w rozwoju programisty i każdy przez nią przechodzi. Jeżeli jesteś początkujący to zapewne zauważyłeś, że gdy mówisz do kogoś ifami i pętlami to on krzywi się i zaczyna rysować jakieś prostokąty połączone strzałkami aby cokolwiek zrozumieć. Jeżeli bawisz się w to już kilka lat to zapewne irytują cię juniorzy, którzy mówią do Ciebie kodem. Normalna sprawa, abstrahowanie przychodzi z czasem ...

Zatem "Specify first" jest dobre na początek gdy świerzbią nas paluszki i chcemy jak najszybciej dorwać się do klawiatury i nastukać coś co można będzie za chwilę radośnie debugować. Analizowanie, modelowanie, projektowanie... to dobre dla zgredów.

Przy podejściu Top-Down rzeczywiście klasy "niższe" mogą być pokraczne bez wyobrażenie sobie jak będą używane. Ale top-down powinien być style analizowania problemu, natomiast projektowanie klas można przeprowadzić bottom-up dzięki czemu powstaną "komponenty" - niczym niezależne części "ruchomej machiny". Z czasem przychodzi umiejętność wyobrażania sobie i manipulowania obiektami "w pamięci" (własnej) bez potrzeby pisania czegokolwiek. Pomocy może być ew. ołówek, kartka i UML. Ktoś pamięta w ogóle co to jest UML? Nie to nie jest kolejna biblioteka do mockowania;P

Taaak.... kiedyś pewnie powstanie Unified Mocking Library - w sumie już nic mnie nie zdziwi w naszej branży...

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

Od razu uprzedzę ew. komentarze aby uniknąć niepotrzebnego niezrozumienia.
W tekście nie zajmuję stanowiska ZA albo PRZECIW testom jednostkowym. Moim celem było zwrócenie uwagi na fakt, że same testy niewiele wnoszą do koncepcji Clean Code. TDD jest techniką implementacyjną oraz od niedawna jest sprzedawany jako technika specyfikowania dla niecierpliwych/początkujących.

Moja teza jest następująca: aby osiągnąć Clean Code potrzeba dodatkowo kilku innych technik developerskich. Dopiero synteza całości może dać zadowalający rezultat.

I niestety trzeba się wysilić znacznie bardziej niż tylko dopisać więcej kodu - testującego. A podstawowe zagadnienia z zakresu inżynierii oprogramowania (Low Coupling, High Cohesion, IoC,...) przydają się również choćby do pisania kodu podatnego na testy - o wysokim poziomie testability.

poniedziałek, 19 października 2009

Mały refreshing

Jako, że na blogspocie pojawiły się nowe szablony (a poprzedni zielony już mi się znudził) uznałem, że czas na zmianę.

Przy okazji lekkiej zmianie uległa sama nazwa bloga (poprzednia brzmiała chyba zbyt pretensjonalnie), motto i sekcja "o blogu".


//================
Zastanawiam się też nad pozbyciem się słowa "holistycznie" z nazwy - ostatnio kojarzy się ono z medycyną alternatywną, homeopatią i innymi tego typu rzeczami;) Ale nie wszystko n a raz.

niedziela, 18 października 2009

JUG, JDD, DDD, Cooluary v3 - podsumowanie



Tydzień upłynął pod znakiem Domain Driven Design.

Wtorek: prezentacja "DDD + impl w Seam" na lubelskim JUG.

Był to beta test przed piątkową ewangelizacją DDD w Krakowie podczas Java Developers' Day. Przy okazji podziękowania za feedback, który otrzymałem od wielu osób po wtorkowym wystąpieniu. Dzięki niemu udało mi się skompresować ponad 2h do 45 min na potrzeby JDD:)

Sobota: COOluary i powtórka DDD. Tym razem już 3h, sam DDD bez Seam - panowie słusznie założyli, że technikalia są drugorzędne.

Zarówno podczas prezentacji na JUG jak i DDD pojawiło się kłopotliwe pytanie odnośnie poniższego slajdu:



Slajd prezentuje Building Block, przy pomocy których to modelujemy domenę biznesową.
Ale zanim przejdziemy do pytania - przyda się wstęp. Jeżeli ktoś był na prezentacji, to może spokojnie przewinąć się niżej...


NIEKRÓTKI WSTĘP

Logikę dzielimy na dwie warstwy:
- Warstwę logiki aplikacji
- warstwę modelu domenowego

Warstwa logiki aplikacji jest cienka nie bez kozery. Zawiera ona logikę specyficzną dla danej aplikacji i jednocześnie nie będącą logiką reguł biznesowych - przykładowo: transakcje, bezpieczeństwo, jakiś mailing, ale również koszyk w sklepie internetowym. Model zamówienia i produktów to logika biznesowa, ale sam koszyk jest ficzerem aplikacji.

Warstwa modelu domenowego jest budowana z klas grających pewne role. Z ról wynika odpowiedzialność tych klas. Czyli opieramy się na paradygmacie Responsibility Driven Design.

Te role to:
Encje - identyfikowalne (posiadająće ID) byty biznesowe. Nie tylko rzeczowniki, ale również np metafory przejścia/zmiany stanu. Co ważne z rolą encji wiąże się realizacja odpowiedzialności biznesowych. Zatem mówimy stanowcze NIE dla anemicznego modelu;)
Inaczej niż w popularnych ORMach encja jak najbardziej powinna mieć metody biznesowe.

Value Objects - obiekty nieidentyfikowalne i zwykle niemodyfikowalne. Jeżeli 2 Vo mają takie same atrybuty to są wówczas tożsame. VO również mogą posiadać odpowiedzialność biznesową. W pewnych podejściach są marginalizowane a w innych wręcz wypierają encje - ale to temat na osobne posta, więc zostawmy.

Agregaty - Encje, które zawierają w sobie grafy encji lub VO. Co ważne agregaty hermetyzują swą implementację więc wszelkie operacje na wnętrznościach wykonujemy przez metody biznesowe encji głównej będącej Aggregate-root.

Repozytoria - zarządzają persystencją Encji/agregatów. To oczywiście jedynie interfejsy; implementacje są warstwę niżej. Nie ma tu metod wyszukujących po kryteriach pochodzących z GUI znanych z DAO.

Fabryki - hermetyzują złożoną (zwykle) logikę tworzenia agregatów.

Servisy - servisy biznesowe (nie mylić z aplikacyjnymi) zawierają logikę, której nie sposób sensownie przypisać do żadnej encji/agregatu/vo.

Polityki - to nic innego jak Wzorzec Strategii. Enkapsulują zmienność/wariacje logiki biznesowej. Jest to kluczowa figura podczas modelowania i rozmowy z expertem biznesowym.

Zdarzenia biznesowe - chodzi po prostu o decoupling artefaktów biznesowych od technicznych obiektów obsługujących. Dodatkowo możemy mieć asynchroniczność.

Przykład agregatu: Zamówienie zawierające pozycje zamówienia:


@Entity
public class Order{
@Id private OrderId id;
@OneToMany private List<OrderItem> items = new ArrayList();
private BigDecimal sum = new BigDecimal(0);
//.... status, createDate, rebatePolicy, productRepository,...

public void add(Product product, int quantity){
OrderItem oi = orderItemFactory.build(product, quantity, rebatePolicy);
//TODO jeżeli produkt jest już na liście to możemy jedynie zmienić ilość w odpowiednim order item
items.add(oi);
sum = sum.add(oi.getCost());
}

public void submit(){
if (status != Status.NEW)
throw new InvalidStateException();
status = Status.IN_PROGRESS;
createDate = new Date();
eventsManager.handle(orderEventsFactory.orderSubmitted(this));
}

public Iterator<OrderItem> getOrderItems(){
return items.iterator();
}
}


Kod zawiera kilka charakterystycznych dla DDD konstrukcji.

  • lista produktów jest prywatna i nie ma do niej gettera a już niedopuszczalny jest setter. Dostęp do listy poprzez iterator, który nie umożliwia zmiany.
    Pamiętajmy, że w JPA nie musimy uzywać getterów/setterów - wystarczy adnotacji na polu, wówczas ORM używa refeksji.
  • operacje biznesowe poprzez odpowiednie metody: add, submit
  • operacja add przyjmuje produkt - szczegółem impl jest tworzenie wewnątrz zamówienia jakiegoś OrderItem
  • operacja add zmienia wewnętrzny stan - co by było gdyby można było zmieniać listę udostępnioną getterem? Kto musiałby pamiętać o zmianie sumy?
  • operacja submit zmienia jakieś pola - jakie? to szczegół. Pojawią się nowe pola w encji Order - wówczas zmieniamy tylko metodę submit. Operacja dodatkowo może sprzeciwić się rzucając wyjątek oraz generuje darzenie.



"KŁOPOTLIWE" PYTANIE
Czy agregat/encja może wołać: repozytorium/servis/fabrykę?
Ogólnie: jakie zależności (pomiędzy czym a czym) są dozwolone w DDD?


Przyznam się, że sam miałem kiedyś tego typu pytania... Nie dawały mi spokoju: jakie są ograniczenia, co z czym można łączyć a co jest zabronione? Co gorsza, członkowie grup dyskusyjnych podawali sprzeczne "złote" zasady.

Zasada jest prosta: możesz robić wszystko co jest dobre dla modelu.
Wszystkie building blocks DDD są równo traktowane - nie ma ważnych i ważniejszych, mogą korzystać z siebie nawzajem jeżeli wynika to z logiki biznesu.

Dobre wyjaśnienie tego problemu znajdziecie w prezentacji Is Domain-Driven Design More than Entities and Repositories? Tak, tak - sarkastyczny tytuł oznacza, ze J. Nilsson będzie lekko kpił sobie z prymitywnych interpretacji DDD typu JavATE;)

Ale jak uniknąć kodu spaghetti w warstwie modelu domenowego jeżeli zaczniemy w nim łączyć wszystko ze wszystkim? Przecież DDD ma chronić nas przed chaosem a nie sprzyjać jego generowaniu.

Podczas COOLuarów doszliśmy do sformułowania dobrej zasady: Jeżeli z logiki biznesowej wynikają pewne powiązania to nie należy ich unikać. Jeżeli logika biznesowa charakteryzuje się pewnych poziomem złożoności - wynikającej z natury danego procesu, to nie unikniemy powiązań. Ważne aby nie wprowadzać dodatkowej złożoności (przypadkowej).

Dobrą metaforą warstwy logiki aplikacji jest scenariusz dla aktorów, którymi są Building blocks. Albo inaczej: kod logiki aplikacji niejako żongluje klockami biznesowymi. Chodzi o to aby czytać taki scenariusz i "widzieć" czystą logikę.
Więcej na ten temat w końcówce posta Understandability.

Jeżeli teraz jeden z aktorów nie chce pobrudzić się i wywołać np repozytorium to musimy zrobić to wprost w głównym wątku scenariusza (a wynik sztucznie przekazać gdzieś do środka aktora). Powoduje to pojawienie się sztucznych zgrzytów w scenariuszu. I zamiast ułatwiać zrozumienie go - utrudnia.

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

J. Nilsson twierdzi, że jest to typowe pytanie zadawane, przez każdego adepta DDD. Wg mnie taka jest kolej rzeczy - w każdej dziedzinie. Wynika to z modelu kompetencji braci Dreyfus gdzie na początku potrzebujemy dokładnych wytycznych jak zrobić. Później uczymy się, że to zależy od tego co chcemy osiągnąć.

piątek, 16 października 2009

MVC Revisited

Kto w 2009r pisze jeszcze o MVC? Czy można jeszcze coś konstruktywnego dodać w tym temacie?

Spróbuję napisać coś może nie tyle konstruktywnego co porządkującego pojęcia... Doczytajcie do końca a być może dowiedzcie się czegoś co uzupełni wasz zasób ciekawostek służących do robienia wrażenia np na niewiastach podczas imprez;)

MVC - styl to architektoniczny odkryty bodajże jakieś 30 lat temu. Zatem należy mu się najpierw rys historyczny: jak to drzewiej wyglądało w Smalltalku...
Widok - czysty rendering
Kontroler - obsługa zdarzeń urządzeń sprzęgu ludzkiego (taki się kiedyś mówiło) typu klik myszką czy naciśnięcie klawisza
Model - dane do prezentacji i logika nimi manipulująca.

Jak to wygląda dziś? Weźmy na ten przykład 2 popularne technologie prezentacji w Javie:

SWING
Czym są kontrolki w Swingu... Widokiem? Nie do końca, ponieważ delegują rendering do rendererów. Sam np JButton jest reprezentacją jakiegoś bytu na GUI - posiada atrybuty typu położenie, ale rysowaniem zajmują się renderery wchodzące w skład aktualnego look&feel. Ale może nie wnikajmy w implementację i dla uproszczenie niech będzie to widok.

Kontrolki mają również swój wewnętrzny model "prezentacyjny". SetText przecież ustawia "coś" we wnętrzu kontrolki. Zwykle kopiujemy to coś z modelu właściwego budując własny framelet (nano framework) wokół Swinga. Ale sam rendering następuje na podstawie wewnętrznego modelu kontrolki.

Przyjrzyjmy się obsłudze zdarzeń... W Smaltalku zdarzenia techniczne obsługiwał kontroler. W Swingu robi to kontrolka, która deleguje odpowiedzialność do listenerów. Czyli to listenery są kontrolerem? Nie zawsze... w niektórych frameworkach Swingowych implementujemy własne kontrolery wołane przez listenery.

Więc co z tym MVC? Wygląda na to, że to kontrolka jako taka implementuje MVC (patrząc z technicznego punktu widzenia)

JSF
W JSF mamy Managed Beans... Obsługują one zdarzenia oraz decydują o nawigacji - czyli kontroler. Zawierają model do renderingu i zbierania danych z formularzy.

A co będzie, jeżeli zbindujemy jakieś kontrolki graficzne z ich komponentami UIComponent (będącymi polami Managed Beana)? Wówczas MB może sterować atrybutami kontrolek graficznych, czyli mamy logikę prezentacji, czyli widok. Znowu wszystko w jednym.


SZERSZY KONTEKST
Aby tego było mało spójrzmy na listę istniejących reifikacji (koncepcji realizacji) idei MVC zgromadzoną w tym oto poście: Interactive Application Architecture Patterns.

Na liście wiele ciekawych pozycji:
- Model-View-Presenter - w podejściu Microsoft (+ Application Controller) oraz Dolphin
- Supervising Controller
- Passive View Pattern
- Presentation-Abstraction-Control

Sam dodatkowo natknąłem się na:
- Hierarchical MVC (framework Scope, który przez parę miesięcy rozwijałem niezależnie od twórców, którzy porzucili coś tak pięknego;)
- MMVC (rozróżnienie na model prezentacyjny + model domenowy)


Wszystkie wymienione podejścia nazwami nawiązują do MVC, ale nim nie są w formie oryginalnej. Czy to źle? Oczywiście, że nie - MVC w swej oryginalnej formie nie przystaje do konstrukcji współczesnych bibliotek i zakładanego przez nie poziomu abstrakcji. Kto np dziś skupiałby się na szczegółach technicznych obsługi zdarzeń generowanych przez urządzenia.


WARSTWY
MVC jest często mylony z architekturą warstwową. Wystarczy zrobić prosty eksperyment myslowy aby uświadomić sobie błąd: zwizualizujmy sobie oba podejścia:
MVC wygląda jak trójkąt.
Natomiast warstwy są jak Ogry;) Albo raczej jak kanapka, tudzież tort.

MVC może być (i zwykle jest) użyty na szczycie stosu warstw jako wzorzec dla samej warstwy prezentacji. Natomiast pod spodem mamy "zwykłe" warstwy, które nic nie wiedzą o żadnym MVC w prezentacji.

W literaturze spotyka się jednak uproszczone podejścia, gdzie składniki trójkąta MVC utożsamiane są z warstwową architekturą, odpowiednio:
V - warstwa prezentacji
M - warstwa modelu (anemicznego najlpiej)
C - warstwa logiki aplikacji
Mówi się wówczas, że MVC jest traktowany jako wzorzec architektury aplikacji a nie jako wzorzec projektowy prezentacji.
Niech i tak będzie... ;)
Byle tylko nie twierdzić, że widok to HTML + CSS, kontroler to PHP a model to baza danych;P

//=================
Post powstał tak a'propos dzisiejszej prezentacji Marka Richardsa na JDD podczas, której wspomniał on o bezmyślnie przyjmowanych dogmatach:)

wtorek, 6 października 2009

Domain Specific Query

Zgodnie z obowiązującym trendem polegającym na prześciganiu się w publikowaniu bardziej lub jeszcze_bardziej oczywistych odkryć - dziś kolej na mnie i na coś na co wpadłem już jakiś czas temu.

Tytułowy wynalazek ma roboczą nazwę DSQ, którą triumfalnie nadałem mu kojarząc dwie modne techniki: Domain Specific Language i Command-query Separation.

NIEKRÓTKIE WYJAŚNIENIE
O CQS pisałem 2 miesiące temu w >tym< poście. W skrócie jest to styl architektoniczny, który zakłada separację API systemu na dwie wyraźne części. Jedna obsługuje (przyjmuje wysyłane do API) Command - rozkazy wykonania operacji. Druga natomiast obsługuje Query - obiekty specyfikujące kryteria pobierania/wyszukiwania danych.

W dzisiejszym poście skupimy się właśnie na tej drugiej części, czyli ogólnie mówiąc na wyszukiwarkach danych. Wyszukiwarki są dosyć proste - ot zwykłe servisy, pod którymi nie znajdują się już żadne kolejne warstwy - chyba, że potrzebujemy jeszcze "dodać coś od siebie" do pobranych z pewnego źródła danych.


DSL to koncepcja dużo bardziej popularna (w sensie ilości publikacji) niż CQS, więc nie będę się zbytnio rozpisywał na jej temat. Ogólnie rzecz ujmując chodzi o to aby kod logiki biznesowej czytać niemal jak język naturalny. Definiujemy sobie język zorientowany na konkretną domenę biznesową a później to już fraszka - program piszę się niemal sam;)
W statycznych językach typu Java nie mamy zbyt szerokiego pola do popisu jeżeli chodzi o zdefiniowanie własnego języka domenowego.

Jedyne co możemy zrobić w Javie to zastosować Fluent Interface i opisowe (samoopisujące wręcz) nazwy metod. Czyli jest to jedynie namiastka DSL, ale cieszy:)

Koncepcja Fluent Interface jest prosta: chodzi o to aby metody danej klasy zwracały instancję obiektu na rzecz którego zostały wykonane. Dzięki temu możemy zamiast pisać tradycyjnie:

Klasa obiekt = new Klasa();
obiekt.metoda1();
obiekt.metoda2();
obiekt.metoda3();

możemy zmienić styl na bardziej fluent:

Klasa obiekt = new Klasa();
obiekt.metoda1().metoda2().metoda3();

Jeżeli teraz zamiast nadawać metodom autystyczne nazwy (w stylu Bruce Eckela), wykażemy się odrobiną inwencji i słowiańskiej fantazji możemy pisać niemalże poetycko:

FabrykaWierszy.stworzUtwor().subtelny().zNutkaDekadencji().rymyNiechBedaCzestochowskie().strof(10);

Chyba lepiej czyta się kod w takim stylu...


DO RZECZY
Po tym przydługim wstępie powinniśmy już mniej więcej czuć czym jest CQS i DSL...

Wyobraźmy sobie następujący problem :

W systemie przechowujemy dokumenty. Dokumenty mają szereg atrybutów: kilka typów statusów (związanych z przepływem dok. jak i technicznych), różnego rodzaju daty, przypisanie do różnych zasobów itp.

Domena biznesowa standardowa i może niezbyt wyszukana, ale dzięki temu wiadomo o co chodzi i możemy skupić się na samej technice.

Serwis, który miałby wyszukiwać dokumenty musiałby mieć metodę o kilkunastu parametrach. Pierwszym problem z taką metodą to wysoki ujemny współczynnik czytalności. Drugi problem to ciągłe zmiany sygnatury metody gdy pojawiają się nowe atrybuty dokumentu.

Zatem dosyć oczywiste jest, że stworzymy sobie klasę przechowującą atrybuty wyszukiwania a nasz serwis będzie miał metodę, do której przekazujemy paczkę kryteriów - po prostu jakiś rodzaj Query z CQS:


public class SearchDocumentsQuery implements Serializable{
//atrybuty wyszukiwania + get/set
}



public class DocumentsFinder{
public Collection<Document> search(SearchDocumentsQuery criteria){
//..
}


Jak do tej pory standard. (Query najpewniej będzie prędzej czy później serializowany.)

Teraz idąc dalej wg utartych szablonów dodalibyśmy do SearchDocumentsQuery kilkanaście pół prywatnych oraz oczywiście nasze ulubione gettery i settery.

Kod klasy klienckiej przy pomocy setterów ustawia kryteria wyszukiwania a kod metody DocumentsFinder.search przy pomocy getterów pobiera te parametry i klei sobie zapytanie w SQL, HQL tudzież mój ulubiony Hibernate Criteria API.

A gdzie miejsce dla DSL? No więc właśnie przy tak ordynarnym podejściu nie uświadczymy.
Załóżmy teraz, że chcemy hermetyzować kod klas klienckich (wywołujących DocumentsFinder)od szczegółów atrybutów Dokumentu.

Klient zamiast operować szczegółami:

SearchDocumentsQuery criteria = new SearchDocumentsQuery();
criteria.setStatus(Status.ACTIVE);
criteria.setCreateDate(...)
criteria.set...
...

Collection<Document> result = documentsFinder.search(criteria);


Wyraża jedynie intencje co do chęci otrzymania dokumentów spełniających zestaw określonych specyfikacji biznesowych:

SearchDocumentsQuery criteria = new SearchDocumentsQuery();
criteria.current().contains("lorem ipsum");

Collection<Document> result = documentsFinder.search(criteria);


Gdzie przykładowo metody DSL wyglądają mniej więcej tak:


public class SearchDocumentsQuery implements Serializable{
private Status status;
private Date epiryDate;
private String[] titleWords;
private String[] contentWords;

public SearchDocumentsQuery current(){
status = Status.ACTIVE;
expiryDate = //data jutrzejsza
return this;
}

public SearchDocumentsQuery contains(String phrase){
String[] words = phrase.split(" ");//lub bardziej wyrafinowy split
titleWords = words;
contentWords = words;
return this;
}

}


Jak interpretować powyższe metody ustawiające kryteria:
current - oznacza, ze życzymy sobie dokumentów będących aktualnie w obiegu.
Co to znaczy od strony modelu: że maja określony status i że wygasają np najwcześniej jutro.

contains - oznacza, że dokument ma zawierać określoną frazę. Z punktu widzenia modelu oznacza to, że tytuł lub treść dokumentu zawiera wszystkie słowa frazy.

Cała idea polega na tym, że klient nie wie nic o polach status, expiryDate, words, ...
Klient wyraża intencję.

Natomiast pola te są dostępne przez gettery dla DocumentsFinder, który to analizuje je i odpowiedzi składa zapytanie. Aby osiągnąć pożądaną widoczność getterów należałoby zdefiniować SearchDocumentsQuery jako klasę statyczną wewnętrzną w DocumentsFinder. Wówczas DocumentsFinder będzie widział metody prywatne, bo takie powinny być rozważane gettery. Statyczną ponieważ chcemy tworzyć Query bez "wywodzenia" jej z instancji Findera.



public class DocumentsFinder{

public static class SearchDocumentsQuery implements Serializable{
private Status status;
private Date epiryDate;
private String[] titleWords;
private String[] contentWords;

//metoda contains i current - pominięto

private Status getStatus(){
return status;
}
}

public Collection<Document> search(SearchDocumentsQuery criteria){
if (criteria.getStatus() != null)//wywołanie prywatnej metody
//dołożenie do zapytania warunku na status
...
}
}



Kuszące może wydawać się dodanie do SearchDocumentsQuery metody buildEecutable(), która produkuje na podstawie stanu swoich wewnętrznych pól (statys, words,...) coś co można wykonać. Tym "czymś" może być PreparedStatement z JDBC, Criteria z Criteria API Hibenrate, Query z JPA, czy zwykły String zawierający SQL albo HQL.

Ale zastanówmy się: czy klasa, która jest widoczna przez klienta (np zdalnego) powinna mieć w sobie kod, który operuje na jakimś API np Hibernate (Criteria API) albo czy powinna wiedzieć coś o jakimś SQL? Przecież Finder może szukać w XMLu lub poprzez web service. Klient nie może tego wiedzieć - ba nie powinien.

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

Pomysł może mało odkrywczy i zapewne wiele osób wpadło na niego równolegle, ale jeszcze nie spotkałem nigdzie opisu.

Pomysł oczywiście nie będzie miał zastosowania w aplikacjach z małą abstrakcją pomiędzy bazą danych a GUI - tak zwanych przeglądarkach danych. W tego typu aplikacjach kontrolki na GUI (w tym pola filtrujące) odpowiadają zwykle niemal dosłownie kolumnom w bazie. W tego typu przypadkach próba abstrahowania od parametrów modelu jest niepotrzebnym uprawianiem dyscypliny pod tytułem nakładanie gaci przez głowę;)

Czekam na opinie, sugestie, krytykę - może być niekonstruktywna:)

środa, 9 września 2009

Ktoś sprzątać musi aby bałaganić mógł ktoś


Wszyscy zapewne pamiętamy burzliwą dyskusją Joela Spolskyego z Uncle Bobem (skrót na infoq)

Odbiła się ona szerokim echem dyskusji na ogólny temat: rzemiosło, profesja, jakość czy moźe lepiej byle jak, byle szybko, byle jako tako zadowolić klienta?

Kto ma rację?

Oczywiście ideałem byłoby dobrze i tanio. Ale jak intuicyjnie czujemy - tak to można tylko w mordę dostać;)

Prawda jak zwykle leży gdzieś po środku.

/*
 * W rozważaniach pomijam specyficzne przypadki typu
 * "firma nieustannie na dorobku"
 * albo specyficzny rodzaj dostawcy
 *
 * Zakładam, że bawimy się w projektach perspektywicznych
 * i jesteśmy świadomi zaciąganego kredytu bałaganu
 */


Ciekawą analizę tego zjawiska oraz racjonalne i pragmatyczne podejście do problemu przedstawił ostatnio w swojej genialnej (i jak zwykle niemiłosiernie ospałej) prezentacji sam guru DDD: Errrrrric Evaaaaans: Strategic Design - Responsibility Traps.

Poruszana tematyka zaczyna być omawiana od 30. minuty, ale gorąco wszystkich zachęcam do obejrzenia całości. Evans po mistrzowsku (i z typowym dla siebie poczuciem humoru) buduje od początku kontekst aby w 30. minucie wygarnąć nam co o nas myśli.
Wcześniej dowiecie się min. jakich strategicznych błędów nie popełniać podczas modernizacji systemu. Okazuje się, że standardowe 3 podejścia są z góry skazane na porażkę.

Prezentacja jest wg. mnie tak dobra, że w moim rankingu zajmuje miejsce 2. - zmieniając tym samym ostatnie notowania.


ZAŁOŻENIA (SAD BUT TRUE)
System jako całość nie może być dobrze przemyślany i zaprojektowany. KROPKA.
Nigdy nie będzie dostatecznie dużo: czasu, pieniędzy, wiedzy biznesowej, ludzi z odpowiednimi kwalifikacjami, czasu, pieniędzy, czasu, pieniędzy, czasu, pieniędzy,...

CO Z TEGO WYNIKA
Mamy 2 możliwości:
a) Wszystko zrobić "byle jak"
b) Pewną część zrobić porządnie i zgodnie z zasadami sztuki (oczywiście kosztem tego, że pozostała część będzie jakości mniejszej niż w punkcie a). W którą część i dlaczego warto zainwestować wysiłek - dowiemy się już za chwilę.

PROBLEM
Evans wytyka często spotykany problem. Ja nazwałbym go "złym rozłożeniem potencjału".
Evans opisuje taki oto często powtarzający się wzorzec: najlepsi (w sensie doświadczenia, intuicji, smaku a nie np certyfikatów) programiści/projektanci/architekci zajmują się tworzeniem tak zwanej "platformy". W zależności od systemu może to być wewnętrzny framework, główne biblioteki - ogólnie najczęściej są to jakieś zawiłości techniczne.

Natomiast reszta teamu - radośni hakierzy odpowiadają zwykle za dostarczanie corowych ficzerów biznesowych budowanych na bazie tejże tworzonej przez lokalnych guru platformy. Oczywiście ficzerów okraszonych zaokrąglonymi rogami w GUI - zgodnie z najnowszą modą żadna kanciasta forma nie jest dozwolona;)

Dostarczają oni funkcjonalności nazwanych przez Evansa sexy capabilities. Robią to tak jak potrafią, czyli byle jak byle szybko:)

Klient jest oczywiście zachwycony nowymi seksownymi możliwościami i nie zważa na marudzenie "nudziarzy od platformy", którzy narzekają, że znowu muszą sprzątać. Zresztą... po co sprzątać skoro działa?
Hero of the day to ten, kto zrobił zaokrąglony guzik zwiększający obroty o 1%;)

Oczywiście przyrost bałaganu jest większy niż możliwości jego sprzątania i mamy problem...

ROZWIĄZANIE: DESTYLACJA
Kto jak kto, ale my Słowianie mamy doświadczenie w destylacji więc mógłbym sobie odpuścić ten rozdział;)




Pomysł Evansa polega na wydestylowaniu domeny corowej. Zacznijmy od tego, że Evans wyróżnia 3 klasy domen:

- Core Domain - są to te specyficzne aspekty biznesu, będące powodem dla którego w ogóle warto tworzyć system. Przykładowo to one warunkują przewagę klienta nad konkurencją, lub odróżniają go od innych. To właśnie w tym miejscu mieszkają "sexy capabilities".

I to właśnie w ten kawałek (a powinien być relatywnie mały) inwestujemy największy wysiłek umysłowy.

To tutaj jest miejsce dla całej artylerii sprawdzonych technik naszego rzemiosła: szczegółowa analiza, archetypy biznesowe, wzorce projektowe, narzut na hermetyzację domeny, narzut na otwartość na rozbudowę, narzut na testability...

Core powinien być dobrze uniezależniony od pozostałych domen, które z definicji są w jakimś sensie "mniej godne zaufania" (bo np niestabilne).

- Supporting Domain - dodatkowe ficzery, bez których jednak można się obyć. Ten kawałek systemu może nawet np outsourceować. Jego jakość z założenia może być niska.

- Generic Domain - specyficzne domeny typu podsystem fakturowania lub biblioteka do operowania na grafach. Najlepiej kupić/uzyć gotowe rozwiązanie. Pamiętając o unikaniu zależności ze strony Core Domain.


Dobrym przykładem ilustrującym relatywizm tego pojęcia w zależności do biznesu jest system komentarzy użytkowników wystawianych kontrahentom.
W ebay jest to corowa funkcja (bez niej nikt nie kupiłby niczego od nieznajomej osoby). Dla amazona to po prostu jakiś poboczny ficzer (supportig domain) - ludzie i tak kupią jeżeli czegoś potrzebują lub po prostu mają ochotę.

Pamiętajmy, że developerzy nie są w stanie (ba, nie powinni) określić co należy a co nie do Core Domain w konkretnym przypadku danego klienta. Określenie Core Domain to jedna z głównych rzeczy, którą trzeba z niego wydusić;)




Chyba już domyślacie się jakie rozwiązanie sugeruje Evans...
Core Team zajmuje się Core Domain.
Najważniejsza część systemu ma szansę być zrobiona zgodnie ze sztuką a członkowie tego teamu zyskują uznanie jako dostawcy "sexy capabilities":)


Mam nadzieję, że moja recenzja/streszczenie zachęci Was (a szczególnie managerów) do poświęcenia godziny cennego czasu na prezentację Evansa.
Ciekaw jestem co sądzicie na temat tego podejścia - liczę na dyskusję równie owocną jak ta z przedostatniego posta:)

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

Prowokacyjnie dodam, że Joel Spolsky stosuje jednak strategię "klasyczną". Flagowe oprogramowanie w ofercie firmy Joela: Copilot (soft dla helpdesku pozwalający na zdalną pracę na maszynie "petenta") to nic innego jak nakładka na OpenSourceowy projekt na licencji GPL ;PPP

Czyli ni mniej ni więcej: ktoś zrobił platformę a partacze Joela dodali "sexy capabilities" - a biznes chyba się kręci:)



I jeszcze druga myśl, która mi się nasunęła: Nierzadko występują takie przypadki, gdy naprawdę doświadczony developer w ogóle nie interesuje się pewną domeną biznesową. Po prostu uważamy (niebezpodstawnie) za interesują mniej więcej w takim samym stopniu jak zeszłoroczny śnieg. Wolimy zamiast tego skupić się na rozwoju w kierunku technicznym z uwagi na jego ogólność wynikającą z abstrakcyjności. Nic na siłę, każdy powinien znaleźć sobie optymalne zainteresowania...

sobota, 5 września 2009

Anticorruption layer

Dziś łikendowy post z serii geek humor.

W ostatnim poście wspomniałem o hermetyzacji modelu domenowego poprzez Anticorruption layer, z którą to komunikujemy się poprzez Command i Query i z której nie wyciekają Encje.

Zobaczmy jak Eric Evans zobrazował to zagadnienie w 14 rozdziale swej książki (strona 367):



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

Więcej tego typu humoru w ospałych prezentacjach Evansa.

czwartek, 3 września 2009

Lazy loading w kontekście paradygmatu Command-query Separation



ODŚWIEŻENIE KONTEKSTU

Kilka miesięcy temu popełniłem dwa posty na temat Lazy loadingu (wiem, że nie chce się Wam ich czytać więc streszczam):

Lazy Loading a sprawa wydajności - post traktujący ogólnie o drastycznym spadku wydajności w sytuacjach gdy LL jest stosowany w niewłaściwym momencie.

Chodzi głównie o klasyczny "n+1 Select problem", który potrafi totalnie zamulić system. W skrócie: problem pojawia się gdy pobieramy z EnityManagera/Sesji hibernate kolekcję a następnie iterujemy po niej i getterami pobieramy zagregowane obiekty. Wówczas do bazy wysyłane jest 1 zapytanie o kolekcję oraz dla każdego z jej n elementów koleje zapytania dociągające potrzebne obiekty.

Podobne schorzenie występuje gdy naiwnie ustawimy w mapowaniu FetchMode na EAGER.

DAO a sprawa Lazy Loading - post poruszający problem tak zwanej cieknącej abstrakcji, czyli "brudzenia" kodu logiki warstw wyższych szczegółami technicznymi typu zamknięta sesji persystencji.

Oczywiście mamy sprawdzony sposób na cieknącą abstrakcję - podejście Open Session in View (rozwiązanie w Springu np przez Mateusza Mirackiego). Niestety w tym przypadku łatwo dopuścić do opisanego powyżej "n+1 Select Problem". Ciekawe rozwiązanie z testowaniem ilości wysyłanych do bazy zapytań przy pomocy statystyk Hibernate zaproponował w komentarzu do mojego posta Milus.


CREDO

Od razu śpieszę wyjaśnić - żeby nie było, że jestem jakimś fanatycznym wrogiem Lazy loadingu czy ORM w ogólności. Wręcz przeciwnie, namiętnie go stosuję od 5 lat i uważam za bardzo wygodny młotek w większości sytuacji.


PROBLEM
Od czasu opublikowania tych postów dostałem kilka maili z zapytaniami o sposoby radzenia sobie z powyższymi problemami.

Pod postami wywiązały się też krótkie dyskusje prezentujące możliwe podejścia.

Właściwie to chodzi głównie o problemem z wydajnością, ponieważ filozoficzny problem cieknącej abstrakcji prawie nikogo nie boli. Nauczyliśmy się z nim żyć i raczej się nad nim nie zastanawiamy (na szczęście Jacek podziela moje rozterki).

PRAWDZIWY PROBLEM
Wg mnie prawdziwy problem leży w samym podejściu do dostępu do danych, czyli architekturze aplikacji. Problem polega na zbytnim uogólnieniu.

Tak samo traktujemy dwa diametralnie różnie rodzaje obiektów:
- obiekty domenowe, które wykonują operacje biznesowe (lub na których to wykonujemy modyfikacje w podejściu proceduralnym)
- dane "przekrojowe" potrzebne jedynie do prezentacji (np wyświetlenia na GUI) wycinka aktualnego stanu systemu

CQS
Teraz nadszedł wreszcie czas na przedstawienie tytułowego bohatera tego posta: zapomniany i zakurzony paradygmat: Command-query Separation.

Paradygmat tez zakłada, że system posiada "interfejs", przez który wysyłamy do niego polecenia oraz osobny, przez który odpytujemy o dane. Nigdy nie projektujemy operacji, które zarówno coś modyfikują jak i odczytują dane.

W jaki sposób możemy wykorzystać to podejście w systemach enterprise?
"Interfejsem" przez który klienty (nie klienci) komunikują się z systemem może być warstwa aplikacji. Cienka warstwa, która zajmuje się wszystkim, oprócz logiki biznesowej i dostępu do danych.






COMMAND
Ok, chcemy coś zrobić w systemie, wysyłamy do niego Polecenie. Tak jak wspomniałem "interfejsem" jest warstwa aplikacji "opublikowana" jako jakieś bezstanowe servisy lub obiekty stanowe - zależnie od wymagań.
Jeżeli klient wyśle do tej warstwy Command, wówczas pobiera ona z Repozytorium jakieś encje (lub agregaty w DDD). Dalej na encjach/agregatach uruchamiamy ich metody biznesowe - jeżeli bawimy się obiektowo, lub wywołujemy jakieś biznesowe servisy przekazując im jako parametry pobrane właśnie encje. Nic specjalnego, klasyczna architektura warstwowa.

Natomiast w tym właśnie przypadku - gdy wysyłamy do systemu Command mający zwykle na celu wykonanie jakiś operacji biznesowych - jak najbardziej możemy (ba powinniśmy) radośnie korzystać z Lazy loadingu. Jest to jak najbardziej właściwy moment ponieważ natura takich operacji jest zwykle taka, że pobieramy kilka obiektów, które wchodzą ze sobą w jakąś interakcję (lub władają nimi servisy). Rzeczone obiekty biznesowe ewentualnie potrzebują do wypełnienia swej biznesowej odpowiedzialności zagregowanych składników. Raczej nic złego się nie stanie, gdy zamiast 3 prostych zapytań do bazy wyślemy ich 5 czy nawet 10.
Czasem nawet będzie to bardziej wskazane niż join - zależy do natury danych.

Cała operacja wykonuje się w obrębie metody z warstwy aplikacji więc jest objęta transakcją i ma cały czas otwartą sesję persystencji więc nie martwimy się o wyjątki Lazy loadingu.


QUERY
Natomiast jeżeli do systemu trafia Query, czyli zapytanie o dane, to wówczas sprawa wygląda nieco inaczej...
Przede wszystkim warstwa aplikacji nie ma pod sobą warstwy z logiką (żadnych servisów biznesowych). Czy potrzebujecie abstrakcji dostępu do danych (DAO/Repozytorium)? Raczej nie zmienicie nigdy źródła danych. Nie ma sensu również testowanie jednostkowe "Finderów" aplikacyjnych z podmienionymi na mocki DAO. Czyli czyste pobieranie danych.

W mniej złożonych systemach możemy sobie pozwolić na zwrócenie encji w odpowiedzi na kwerendę.
W bardziej poważnych raczej nie możemy pozwolić sobie na ujawnianie klientom naszego modelu, więc zwrócimy jakiś Data Transfer Object (DTO). Hermetyzacja modelu to podstawa - dzięki temu może on ewoluować iteracyjnie bez obaw o zniweczenie pracy teamu dospawującego prezentację.


QUERY ZWRACAJĄCE ENCJE
Jeżeli zdecydujemy się na zwracanie encji to musimy uporać się z paroma problemami technicznymi:
- wydajność: zwykle scenariusz obsługi kwerendy to pobranie kolekcji danych. Wówczas mamy jak w banku opisany na wstępie "n+1 Select problem". Rozwiązanie jest bardzo proste - wystarczy się pofatygować i napisać zapytanie z klauzulą JOIN FETCH. Przykładowo SELECT p FROM Person p JOIN FETCH p.addresses - dzięki temu chciwie/łapczywie (nie wiem, na które rozkoszne tłumaczenie się zdecydować) pobierzemy osoby wraz z podciągniętymi adresami. Po prostu ORM wygeneruje SQLa z JOINem.

Niezbyt dobrym pomysłem jest ustawienie w mapowaniu powiązania obiektów z FetchMode.EAGER. Spowoduje ono, że zawsze wyciągając jeden obiekt pobierzemy jego "dziecko". Owszem są sytuacje, gdzie z kontekstu biznesowego takie podejście jest sensowne, ale zwykle stanowią zdecydowaną mniejszość. Zwykle w jednym Use Case zależy nam na pobraniu np osób z adresami a winnym adresy są zbędne.

Warto pamiętać, że domyślnie strategia EAGER obowiązuje dla powiązań wiele-jeden, jeden-jeden i warto ją wyłączać.

- Open Session in View - podejście to o ile jest wygodne to niestety pozwala łatwo zapomnieć o tym, że leniwie podciągamy jakieś dane. Po prostu istnieje niemała szansa, że na widoku odwołamy się do adresów osoby a w zapytaniu zapomnimy dopisać JOIN FETCH. Działa? Działa. Muli? W środowisku developerskim z małą ilością danych pewnie nie;P

- Ilość danych - zwykle Use Case gdzie do systemu trafia Query zakłada, że z bazy trzeba pobrać dane "przekrojowe". Czyli dane z wielu tabel, ale z każdej z nich interesuje nas zaledwie kilka kolumn. W małych systemach, gdzie warstwa GUI i warstwa aplikacji stoją na tej samej JVM będzie to w śmigać.

Ale nawet w takiej konfiguracji mamy problem z pobieraniem zbędnych danych. Przykładowo: gdy pobieramy z bazy np dokumenty aby jedynie wyświetlić ich listę (data, autor) a każdy z nich ma kolumnę przechowującą dziesiątki stron textu. Rozwiązaniem jest leniwe ładowanie pól - czyli ich nieładowanie:) W hibernate wymaga to poddanie skompilowanego bytecodu instrumentalizacji.

Innym podejściem może być zamapowanie tabeli przez kilka klas. Przykładowo DocumentFull, DocumentLight, itp... Jeżeli czujecie niesmak na myśl o mnożeniu bytów to nie jesteście sami.

Hibernate pozwala na pobieranie danych wprost do DTO. Tworzymy DTO szyte na miarę danego Use Case, a składnia wygląda tak:
SELECT new pakiet.KlasaDTO(pole1, pole2.podpole) FROM...
Oczywiście przy założeniu, że odpowiedni konstruktor istnieje.

QUERY ZWRACAJĄCE DTO
Jeżeli zdecydujemy się na zwracanie DTO to zapewne dlatego, że potrzebne dane są na tyle przekrojowe, że żaden zestaw encji nie modeluje ich sensownie (i optymalnie).
Innym powodem może być chęć hermetyzacji zmiennego modelu poza stabilną anticorruption layer. Warstwa zapobiegająca gniciu to pojęcie z DDD i ma pragmatyczny sens w nieco bardziej perspektywicznych projektach.

Poza tym możemy być dumni, że nawet nasza architektura wspiera Agile umożliwiając ewolucję modelu domenowego bez rujnowania wszystkiego dookoła:)

Częstym błędem w tym przypadku jest pobieranie z ORM encji a następnie przepakowywanie ich w DTO.
Nie tędy droga...


Pobierzmy tylko to co tak na prawdę jest potrzebne. Najprościej zrealizować to z użyciem wspomnianej konstrukcji Hibernate SELECT NEW. Jednak w złożonych systemach zwykle nieodzowny będzie co najmniej w paru jakiś szyty na miarę i zoptymalizowany SQL. Jakąś abstrakcją nad SQLem może być wówczas np iBATIS mapujący result na DTO.


Jak to zwykle bywa najlepsze będzie podejście hybrydowe. Tam gdzie możemy na to sobie pozwolić zwracamy w wyniku obsługi Query encje - zwiększając tym samym swą produktywność. Natomiast tam gdzie krytyczna jest wydajność lub specjalna struktura danych, zwracamy DTO.


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

Opisane powyżej aspekty wydajności związane z Lazy Loadingiem aplikują się do systemów każdej wielkości - szczególnie "n+1 Select problem". Natomiast rozwiązanie z CQS a szczególnie podejście gdzie zwracamy z kwerendy DTO jest bardziej pracochłonne, przez co aplikuje się do projektów bardziej perspektywicznych.

Ale czy jest sens tworzyć w JEE projekty inne niż perspektywiczne? Do prostych i szybkich zadań typu "przeglądarka do bazy" jest przecież Microsoft Access;P

poniedziałek, 31 sierpnia 2009

Dzień bloggera



Ktoś sobie wymyślił, że dzisiaj będzie światowy dzień blogerów. Ok, fajnie.

Aby jakoś uczcić ten niezwykły, magiczny i jedyny w roku dzień postanowiłem zarekomendować nowego bloga poświęconego Javie i IT ogólnie: Technology For Human

Co prawda z założenia blog ma zawierać solidne tutoriale dla początkujących, ale na ile znam autora, to nawet doświadczeni wyjadacze będą mogli dowiedzieć się czegoś nowego z każdego posta.

Autor postawił sobie za cel przekaz rzetelnej wiedzy popartej realistycznymi przykładami. Bez ściemy znanej z tandetnych tutoriali. Autor zapewnia, że przykłady będą pochodzić z placu boju.

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

Przyznam się, że sam miałem kiedyś w planie serię postów na temat własnej "skrzynki z narzędziami" - czyli kilku różnych kompletnych zestawów narzędzi developerskich oraz frameworków, które wykorzystałbym w przypadku problemu danej klasy. Niestety z braku czasu wciąż odkładam to zadanie. Ale na szczęście pojawiła się blog kolegi więc chyba sobie odpuszczę:)

Hasło na zakończenie tego magicznego i jedynego w roku dnia: Blog to zdrowie (a oto dowody tej tezy)

środa, 26 sierpnia 2009

Jeszcze prostota czy może już prostactwo?


Chyba każdy z nas zastanawiał się nad tym co takiego urzekło go w programowaniu, że wybrał właśnie to zajęcie. Co takiego przykuwa nas na długie godziny do komputera i powoduje, że zapominamy o zwyrodnieniach kręgów szyjnych i nadgarstków, o zakrzepach w żyłach głębokich,...

Niektórzy odpowiadają, że pociąga ich możliwość tworzenia i kreowania. Inni lubią mieć kontrolę i możliwość decydowania. Są też tacy, którzy pławią się w masochistycznej rozkoszy (choć nigdy się do tego nie przyznają) lawirowania pomiędzy ograniczeniami różnego rodzaju.

/*
 *Przypadki gdy ktoś po prostu "miał Pascala na studiach"
 *i przypadkiem został zatrudniony z jakiejś "łapanki"
 *a później "tak już zostało" pominę w tych rozważaniach.
 */


Jest jednak jeden wzorzec, który powtarza się byt często aby go zignorować. Mianowicie - większość z nas podkreśla, że największą przyjemność sprawia poszukiwanie prostoty, elegancji i harmonii. Nasze umysły nieustannie poszukują zgrabnych modeli pewnych wycinków modelowanego świata. Nic nas tak nie cieszy jak elegancka i prosta forma.

Po tym przydługim wstępie czas przyznać się co sprowokowało mnie do napisania tego posta: prezentacja Jonathana Dahl Programming and Minimalism.

Szczerze polecam tą prezentację (to tylko ok pół godziny). Pomimo ogromnego stresu w jakim był prelegent jest to aktualnie prezentacja numer 2 na mojej liście prezentacji dla programistów. Wyprzedza ją tylko znakomita Developing Expertise: Herding Racehorses, Racing Sheep o której pisałem tu: The Unforgiven.

Jonathan Dahl dokonał czegoś niezwykłego i pięknego zarazem. Pokusił się o próbę syntezy zasad minimalistycznej prostoty, występujących w języku naturalnym, muzyce oraz programowaniu oczywiście. Szczególnie ciekawie brzmi część przedstawiająca przekrojowo sinusoidalne zmiany komplikacji w muzyce - począwszy od Mozarta a skończywszy na Punk Rocku:) Być może dowiecie się z niej dlaczego pewne gatunki muzyki Wam odpowiadają a innych kompletnie nie trawicie... wszystko zależy od preferowanego poziomu komplikacji "figur" akustycznych.

Ale wracając do kontekstu programistycznego... Prezentacja, którą się tak zachwycam traktuje o programowaniu stricte w dosyć zdawkowym wymiarze. Ot mamy jakąś funkcję w Ruby - pogańskim języku, którego nie rozumiem. Jedna jej wersja została upstrzona składnią, którą zna pewnie 5% programistów tegoż języka, natomiast druga (o identycznej funkcjonalności) zawiera prostego ifa. Wniosek: prostota w programowaniu to podstawa. Dziękujemy i do widzenia.

Chciałbym jednak rozwinąć ten temat, ponieważ z moich obserwacji wynika, że o ile wszyscy powtarzają jakoby prostota była ich świętym Graalem to samo pojęcie prostoty w programowaniu jest mocno rozmyte. Nieraz spotkałem się z sytuacją gdy pewien mniejszy lub większy kawałek kodu był oceniany jak majstersztyk lub kupa guano.

Od czego to zależy?
Nawet w tak dobrze zdefiniowanej domenie jak muzyka mamy sytuacje, gdy pewien utwór/twórca/gatunek/styl "się podoba" lub "się nie podoba". Klasycznym przykładem jest Chopin, którego się uwielbia albo nienawidzi:)
Odwołując się do wspomnianej wyżej prezentacji: zależy to od stopnia komplikacji i możliwości jej ogarnięcia przez odbiorcę. Pewne formy są zbyt banalne, pewne zbyt skomplikowane (wydają się wręcz chaotyczne) a pewne w sam raz nam odpowiadają.
Większość umysłów ścisłych jest jak inż. Mamoń - podoba im się to co już znają. Mechanizm jest prosty: znając jakiś wzorzec (utwór) wiemy czego się spodziewać w danej chwili i cieszymy się gdy słyszmy właśnie to czego się spodziewamy. Przypomnijcie sobie jak irytująca jest nagła zmiana kawałka albo różnego rodzaju zakłócenia;P

Wyobraźmy sobie przykładowy problem, którego rozwiązanie można zapisać w jednej klasie z jedną metodą, która ma powiedzmy 10 ekranów a na nich 100 ifów. Ktoś może powiedzieć, że jest to rozwiązanie najprostsze, ponieważ "wszystko po kolei widać", wystarczy się dobrze wczytać;)
Inne podejście może wymagać powiedzmy 5 interfejsów i 10 klas. Każda klasa zawiera parę linijek kodu. Ktoś może stwierdzić, że jest to rozwiązanie zbyt skomplikowane ponieważ aby zorientować się co się dzieje, "trzeba skakać po wielu plikach" i "nie widać całego przebiegu".

Kto ma rację? To oczywiście zależy od preferencji. Jedni wolą wiele prostych pudełek składających się na bardziej wyrafinowaną strukturę, inni będą preferować jedno pudło, w którym jakoś się połapią.


Aby nie szukać konkretnego przykładu zbyt daleko przyjrzyjmy się strumieniom w standardowej bibliotece Javy. Pamiętacie jeszcze swoje pierwsze z nimi spotkanie?;P Dziesiątki klas, jakieś interfejsy, pokręcone konstruktory. Pierwsza reakcja? Panika i odrzucenie. Ja chcę przecież tylko odczytać głupi tekst z pliku a to panie takie pokomplikowane.

Natomiast znając pewną ogólną zasadę - wzorzec dekoratora całość nagle okazuje się być piękna w swej prostocie. Trudno stwierdzić co cieszy bardziej: elegancja tego rozwiązania czy prostota używania i niezwykła łatwość składania złożonych strumieni.

//===========================
Problem prostoty nie jest jednowymiarowy. Nie zawsze problem polega jedynie na tym po prostu działało. Często musi jeszcze dać się zrozumieć, zmienić, rozbudować, skalować, optymalizować, itd. Wówczas kryteria prostoty i elegancji zmieniają wartości.

Bardzo łatwo wówczas pomylić szlachetną prostotę z pospolitym prostactwem. Linia pomiędzy nimi jest zwykle cienka.

Słynny cytat Einsteina "rzeczy powinny być tak proste jak to możliwe" niesie ze sobą na prawdę głębokie przesłanie...

niedziela, 5 lipca 2009

Jakość (życia)


Kolejna Javarsovia za nami.
Brawa dla organizatorów za profesjonalizm i samą inicjatywę. Podziękowania należą się też (a właściwie to przede wszystkim) prelegentom, którzy altruistycznie dzielili się wiedzą i doświadczeniem.

W stosunku do poprzedniego roku zauważam pozytywną zmianę patrząc na przekrój tematów. Zdecydowanie mniej onanizmu technicznego. Nowe zabawki nie rozwiązują problemów, oprócz tych, które same stwarzają. Stawiamy bardziej na profesję. To chyba znak, że idą zmiany w mentalności naszego comunity - zgodnie z nowym trendem: craftsmanship.

W większości prezentacji, w których uczestniczyłem pojawiała się idea jakości. Jakości rozumianej nie tylko jako atrybut produktu, ale przede wszystkim jako jakość naszej codziennej pracy.

Z jakością produktu bywa różnie - nie zawsze jest istotna. Klient nie jest świadomy - dla niego ma po prostu działać. Szef martwi się o harmonogram i budżet - o inne sprawy będziemy (będziecie;P) martwić się później. Biznes rządzi się swoimi prawami i to jest zrozumiałe.

Natomiast aspekt jakości rozumianej jako jakość codziennej pracy nad projektem IT to coś z goła innego. Chyba nikt nie lubi tego uczucia, gdy budzisz się rano, otwierasz oczy, patrzysz w sufit a pierwsza myśl jaka przychodzi do głowy na dobry początek nowego dnia to "k@#$%, nie po to studiowałem x lat żeby teraz wstawać rano, brać widły do ręki i przerzucać ten zgniły kod".

W wymiarze osobistym jakość codziennej pracy jest jedną z najważniejszych składowych jakości życia.

Chyba stąd u prelegentów, którzy swoje w życiu przerzucili, refleksje na tym co robić aby nie szarpać się w codziennej pracy. Krótka relacja z prelekcji, w których uczestniczyłem:

Randori TDD - Krzysztof Jelski - ciekawe podejście do developemntu. Randori w skrócie (i na ile udało mi się zrozumieć) polega na programowani w parach z rolą kierowcy i pilota. Kierowca programuje skupiając się na detalach podczas gdy pilot dba szerszą wizję.
Programowanie odbywa się w cyklach: najpierw test, działająca implementacja, refaktoryzacja do osiągnięcia jakości rozwiązania. Rozpoczęcie programowania od testu powoduje, że zastanawiamy się nad tym JAK nasza klasa będzie używana przez klasę-klienta.

Co prawda wymaga to zmiany mentalnej, ale spojrzenie z perspektywy klasy-klienta na prawdę się opłaca. Chyba każdy nie raz miał przykre uczucie niesmaku próbując wreszcie użyć swój super zestaw właśnie stworzonych klasek.
Przewiduję również wzrost produktywności - ciężko obijać się gdy siedzi ktoś obok nas;)

Wg mnie jest również doskonały sposób na efektywne przekazywanie wiedzy.
Najszybciej uczymy się przez naśladownictwo. Zjawisko to może zostać spotęgowane przez obecność eksperta, który na bieżąco wyjaśnia rozwiązania problemów i przesłanki stojące za wyborem danego podejścia. Najbardziej efektywnym i przynoszącym najlepsze rezultaty sposobem na wprowadzenie nowego członka do zespołu jest poświęcenie mu czasu na wspólne programowanie. Niemal naturalnie przejmuje on wówczas nawyki i dobre praktyki, których samodzielne wypracowanie zajmuje niejednokrotnie lata.

Drools Guvnor - Jarosław Kijanowski - tutaj aspekt jakości pracy nie był zbyt silnie uwypuklony. Może poza aluzjami do poziomu profesjonalizmu analityków;)
Na na podkreślenie zasługuje sama forma prelekcji: z jajem, bez stresu, ogólna idea-zaciekawienie-przykład. O to chodzi!

Praca z odziedziczonym kodem - Jakub Dziwisz przede wszystkim brawa na Jakuba (agiletuning) - profesjonalny prelegent z charyzmą i klasą. Praca z odziedziczonym kodem, hmm temat w sumie bardzo smutny;) Zaprezentowano techniki refaktoryzacji, mające na celu zmniejszenie bólu. Pamiętajmy jednak, że słaby kod to nie tylko problem przeszłości. On powstaje również tu i teraz zatem przedstawione techniki mogą mieć wykorzystanie w codziennej pracy. Podstawa to dobrze zrozumiany OOD, G.R.A.S.P i S.O.L.I.D oraz wzorce projektowe w granicach rozsądku.

Ewolucja Architektury - Paweł Lipiński. Paweł zaprezentował pragmatyczne podejście do architektury. Architektura - arcy ważny składnik procesu a z dyskusji uczestników widać było, że są różne sposoby rozumienia tego słowa. Przydało by się uporządkowanie pojęć: wskazania domen architektury (aplikacji, systemy, wdrożeniowa) oraz jasnego rozdzielenia architektury od projektowania.

Najważniejsza wg mnie myśl z prelekcji: architekci muszą programować. Jako architekt potwierdzam i podpisuję się pod zdaniem Pawła. Niestety aby móc powiedzieć coś sensownego na temat kształtu systemu trzeba ubabrać sobie rączki. Nie wystarczy mieć doświadczenia x lat temu, a już kuriozalna jest sytuacja gdy architekt może pochwalić się jedynie programikiem w Pascalu napisanym na studiach.

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

Prezentowane zagadnienia poruszyły część aspektów jakości (w sensie bezstresowej pracy) w całym procesie wytwarzania softu.
Do kompletu, jako uzupełnienie, dodałbym:
- Analizę biznesową - zrozumienie problemu klienta, zebranie wymagań
- Rzetelną analizę systemową - analiza to proces myślowy, włożenie wysiłku w uporządkowanie biznesu. Warto wesprzeć się sprawdzonymi rozwiązaniami, takimi jak Archetypy biznesowe. Niestety czasem jest to jedynie spisanie ton dokumentów zawierających nieprzetrawiony bełkot.
- Architektura - podczas prelekcji Pawła padło pytanie o przykłady architektur. Wszystko oczywiście zależy od konkretnego problemu Ja obecnie w nietrywialnych aplikacjach webowych stosuję modyfikowane do problemu architektury Distributed DDD oparte o Command-Query Separation.
- Projekt - Architektura narzuca ogólny styl projektowania. Aby uniknąć w niedalekiej przyszłości koszmaru utrzymania własnego kodu warto wspierać się technikami OO takimi jak SOLID czy GRASP.
Ale podstawa to w ogóle projektować zamiast rzucać się jak dzikus na klawiaturę i radośnie napieprzać;)

środa, 1 lipca 2009

Ile Ci to zajmie? (różnice w postrzeganiu czasu)


Niektóre filozofie zakładają, że czas nie istnieje. Jest tylko tu i teraz, upływ czasu to złudzenie. Inni z kolei chcą go wyginać i zaginać.

Ale zejdźmy na ziemię. W biznesie czas to tylko pieniądz. Stąd też najczęściej pojawiającym się pytaniem w komercyjnym procesie tworzenia softu jest nasze ulubione:

ILE CI TO ZAJMIE?

Managerowie uwielbiają zadręczać tym pytaniem swoich/swoje zasoby. Irytują się, gdy developerzy nie są w stanie odpowiedzieć na tak elementarne pytanie. Przecież jeżeli robi się coś od kilku lat to powinno dać się niemal co do godziny określić ilość potrzebnego czasu. Co za durnie, siedzą w tym po uszy i nie potrafią nawet zarządzać sobą, swoimi zadaniami i swoim czasem. Przecież programowanie jest prawie jak budowanie. Macie nawet te swoje wzorce pokrakowe (czy jakieś tam) oparte na latach doświadczeń budowlańców. Muszę mieć harmonogram i się z niego wywiązać. Budżet Panowie! Budżet!

Developerom z drugiej strony ciśnie się na usta "Nie wiem! Nigdy wcześniej tego nie robiłem!" - heh ale tylko Ci o mocnej pozycji w plemieniu mogą sobie pozwolić na zwerbalizowanie swoich myśli. Czy on nie rozumie, ze praca badawczo-rozwojowa (a taką właściwie jest każdy nowy projekt z uwagi na nowe narzędzia) nie jest liniowa a eksponencjalna. Stając w kopalni z kilofem w ręku, kilometr pod ziemią i fedrując na przodku nigdy nie wiadomo kiedy trafimy na skałę, ile będzie trwać stagnacja i kiedy pojawi się nagły wybuch ruszający wszystko do przodu. Programowanie to nie murowanie muru z cegieł (1 cegła na minutę = > mur z 1000 cegieł to 16.7 godziny) - to tak nie działa.

Jedni i drudzy mamroczą pod nosem: co za durnie!



Zagadnienie szacowania czasu, metodyki szacowania to temat na osobna bajkę, dziś będzie o postrzeganiu czasu.

Ale zanim przejdziemy do corowej warstwy posta, zatrzymajmy się na chwilę aby pokazać sceptykom, że postrzeganie czasu rzeczywiście jest względne i każdy doświadcza tego niemal codziennie (proszę nie uciekać od ekranów - względne w sensie poznawczym a nie w sensie teorii względności).

Pomijając filozoficzną i fizyczną naturę czasu, Twój mózg jest po prostu tak skonstruowany, że postrzega upływ czasu. Mamy wbudowane 2 "koprocesory czasu" - zegary dobowy i bieżący. Zegar bieżący opiera się na mechanizmie taktowania (tysiące neuronów) i próbkowania (neurony gwiaździste i piramidalne). Co ciekawe i istotne dla nas, hormony stresu zmieniają stężenie neuroprzekaźników w naszym "koprocesorze" czasu bieżącego co powoduje zmiany w "tykaniu" a co za tym idzie próbkowaniu bodźców ze świata.

Wszyscy znamy uczucie dłużenia się czasu lub odwrotne do niego - szybki upływ czasu. Jak się okazuje niem tu żadnej magii - wszystko zależy od poziomu hormonów stresu. Dzięki temu np w stresowej sytuacji na drodze mamy "gęste" próbkowanie świata, wchłaniamy więcej danych w jednostce czasu i mamy więcej "cykli" na podejmowanie działań związanym z uniknięciem zderzenia z jakimś durniem.


Przejdźmy do meritum, czyli czasu w kontekście IT.
W tym celu zostawmy poziom "techniczny" czyli neurony i przyjrzyjmy się ogólnym strategiom przeżywania czasu: Spontaniczny plan dnia.
Wynika z niego, że dysonans w podejściu do czasu i planowania jaki obserwujemy na różnych stanowiskach może wynikać z różnych sposobów myślenia o czasie. Tak, tak, wg tego modelu różnimy się wewnętrznymi reprezentacjami czasu!

Mamy zatem ludzi żyjących:
- W czasie (In time) - żyją chwilą, odczuwają każdy moment, bez planu i kontroli czasu, skupiają się na aktualnym działaniu
- Pomiędzy Czasem (Between Time) - w pozycji zdystansowanej do czasu, mają na niego ogólny ogląd, widzą możliwości działania, ale nie przypisują im czasu trwania, dowolnie przestawiają zadania
- Poprzez Czas (Through Time) - również z dystansem, ale mając ścisły chronologiczny plan, każde zdarzenie ma czas rozpoczęcia i czas trwania, czas jest linią o ograniczonej pojemności (w sumie logiczne;)

Autorzy artykułu zachęcają nas do trenowania różnych sposobów przeżywania czasu i dobierania najlepszego do danej sytuacji. Powodzenia.

Zastanawiam się czy można w ogóle być dobrym managerem nie żyjąc Poprzez Czas - widząc dokładny plan, zależności czasowe i być przez to cel-oriented?

Czy można w ogóle prowadzić jakąkolwiek kreatywną działalność (mam na myśli sensowną działalność a nie np malarstwo współczesne) nie żyjąc Pomiędzy Czasem - dostrzegając możliwości i potencjał.

A żyjąc chwilą W czasie lepiej chyba nie męczyć się z IT, rzucić to wszystko i rozpocząć karierę w biznesie porno;)

Każdy powinien zająć się tym w czym jest dobry. A szacunek to podstawa.

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

"Ty masz zegarek, ja mam czas"
...Usłyszał w odpowiedzi na nerwowe pokazywanie na cyferblat podróżnik, gdy usiłował ponaglić swego przewodnika na postoju, pod­czas wędrówki gdzieś, na andyjskim bezludziu.

piątek, 5 czerwca 2009

Domain Driven Disaster

Przygotowania do prezentacji o DDD na LJUG w toku. Tymczasem mamy niecodzienną okazję aby zobaczyć jak w praktyce możne wyglądać projekt, którego jednym ze szczytnych założeń było podążanie za DDD.

Dziś pierwszy (i mam nadzieję, że nie ostatni) post z gatunku gościnnych.

Jako pierwszego mamy zaszczyt gościć tajemniczego Malory z krainy deszczowców, który podzieli się doświadczeniami z dużego, zagranicznego projektu. Projektu, w którym niestety nieco rozminięto się z filozofią DDD, TDD i Agile:

<post_goscinny>
Przyjemność bycia częścią projektu napędzanego czystym agilem (iteracje mamy, lecz do Agile nadal daleka droga), z domieszką domain driven, test driven, i ogólnie driven skłonił do podzielenia się kilkoma refleksjami. Tym gościnnym postem u Sławka podzielę się refleksjami o dysonansie miedzy filozofią którą niosą ze sobą ddd, Agile, TDD a praktyką... czyli Projektem.

O ddd Sławek pisał tutaj już nie raz i każdy z czytelników mniej lub bardziej "czuje' o co w ddd chodzi. Esencja to słowa z ddd.org: "ddd is a way of thinking and a set of priorities, aimed at accelerating software projects that have to deal with complicated domains". Nic dodać, nic ująć. Koncepcja a raczej filozofia spojrzenia na problem, na domenę. A w praktyce? Bywa różnie. Brak zrozumienia idei którą niesie ze sobą ddd, i twarde (beton) myślenie przez pryzmat aplikacji CRUD driven (formularzyki), gwarantuje Monthego Pythona każdego dnia. Sam wybór ddd nie zawsze jest szczęśliwy bo jak wyraźnie wskazuje cytat: "complicated domain". Więc po co się męczyć, i udawać ddd gdy chcemy zrobić nakładkę na db? Efektem końcowym będzie i tak mieszanka anemicznych encji i a'la serwisów żonglujących encjami w górę i w dół. W praktyce ddd niebezpiecznie zbliża się do kilku „warstewek” klas delegujących wywołania metod z dao. I nie ma w tym nic złego – to rozwiązanie dobiera się do problemu. Nie odwrotnie. Sama sytuacja w której kończymy z pustymi encjami (@Entity, nie żadne tam encje ddd) i serwisami wskazuje że coś jest nie tak. Fundament OOD jest prosty – odpowiedzialność a get/set to chyba jednak trochę za mało...

Siłą napędową ddd to domena, co w odniesieniu do projektu oznacza eksperta domeny, będącego światłem w tunelu dla zespolu. Jako ze projekt nie zawsze ma domain eksperta - bywa śmiesznie. Developerzy to cwani ludzie i dadzą radę, ale to raczej Developer Driven Design (co oczywiście nie jest złe.. ale zależy od doświadczenia zespołu...). W przypadku bogatej domeny, bez pomocy eksperta wątpliwe jest stworzenie nawet poprawnych zrębów domeny, nie wspominając nawet o niuansach i bliższym nakreśleniu relacji w domenie. Pora teraz na agile (fonetyka naszego rodzimego języka, nie żaden tam edżajl).

Skomplikowana domena powoli ewoluuje, nieraz zamiast ewolucji doświadczyć możemy rewolucji, gdy powolny knowledge crunching zaowocuje przełomem w spojrzeniu na domenę. I czy nie jest to "Responding to change over following a plan" który niesie ze sobą agile manifesto? W tym momencie przypomina mi się pytanie podczas jednego z pierwszych spotkań (spotkanie a raczej meeting jest nieodzownym elementem każdego poważnego agile projektu), a mianowicie "kiedy dostaniemy skończony model domeny?". Hmm - odpowiedź była prosta: nigdy. Po czym zaczęło się wyjaśnianie (okraszone nerwowymi spojrzeniami "góry"), że to nie waterfall. Same iteracje nie sprawią, że będziemy Agile. Do tego potrzeba tzw. bigger picture i zmuszenie całego zespołu (łącznie z managementem) do innego spojrzenia na problem. Iteracje i milestony mogą być jednak wystarczającą dawką agilu. O tym, że to wymagania napędzają development niekoniecznie się pamięta. Zresztą, bywa w niektórych projektach że wymagania odpowiadają poziomem testom (assertTrue(true)); //FIX LATER, its friday, 4 o'clock ). Raz zrobiony task (zrobiony w stylu general concept, bo jak inaczej bez wymagań...) zostaje jako skończony i nie ma nawet mowy o refaktoringu pózniej... To przecież zbędne filozofowanie... W wyniku zostaje brązowa (flagowy kolor branży) papka kodu którą ktoś (nieszczęśnik, albo student, niewolnik IT) będzie miał przyjemność poprawiać. Domena zamiast ewoluować - kostnieje, zamiast odpowiadać na zmiany - napierdalamy aby zdążyć w terminie.

Ostatnim kawałkiem układanki jest TDD. Który doskonale wpasowuje się do mieszanki DDD i Agile. Domena powinna ewoluować, dojrzewać, i odpowiadać na zmiany. Tutaj pomocną rączkę wyciąga TDD. Pod warunkiem że napisane testy mają sens. W życiu bywa jednak rożnie, w praktyce testy nie zawsze testują. Mądrze napisane uspokajają sumienie i pozwalają pracować nad aplikacją. Pozwalają na eksperymentowanie i dają ujście wiedzy którą powoli team zdobywa. Testy trzeba jednak umieć pisać.. i mieć na nie czas. Z pierwszym bywa rożnie, na drugie niekoniecznie jest czas w schedule projektu. Efekt - każdy boi się panicznie dotknąć czegoś większego w domenie aby całość się nie rozleciała...

Na koniec link do doskonałej prezentacji o... samochodach. Obejrzyj i popatrz przez pryzmat naszej branży. Tutaj też są projektanci i inżynierowie. W tej branży jest miejsce na pasję, rzemiosło i jakość. Uwolnijmy się od brązu.



</post_goscinny>



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

Jak wiadomo najlepiej uczyć się na błędach:) Dobrze, gdy mamy okazję z dystansu przyjrzeć się cudzym:]
Potwierdzają się moje przemyślenia na temat metodyk Agile. Idea piękna, ale pod warunkiem, że mamy dojrzały i świadomy zespól profesjonalistów, którzy wiedzą co robią lub przynajmniej mogą poprowadzić duchowo resztę teamu.

Sama filozofia DDD o ile nie jest jakoś specjalnie wysublimowana to niestety posiada pewną barierę wejścia: podstawy OO - i nie mam tu na myśli składni dziedziczenia, lecz myślenie obiektami.