Pokazywanie postów oznaczonych etykietą Architektura. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą Architektura. Pokaż wszystkie posty

niedziela, 17 lipca 2016

Prezentacje z Devoxx

W dniu poprzedzającym konferencję wstałem o 5 rano i udałem się...
STOP!
To nie będzie sprawozdanie z wycieczki do muzeum z IV klasy

Dzięki dobrej woli Grześka Dudy mamy dostęp do pełnych prezentacji już miesiąc po konferencji.

Lista moich faworytów:


Neal Ford - Why does Yesterday's Best Practice Become Tomorrow's Antipattern?


Piękne, wysokopoziomowe ujęcie naszych przyziemnych problemów.


Wojciech Seliga - Ten lessons I painfully learnt while moving from software developer to entrepreneur



Wojtek ma niesamowity talent do trafiania w punkt z refleksją i radą. Myślę, że powinien spisać je w formie dedykowanej strony "poradnikowej" albo apki, która losowo codziennie przypomina jedną mądrość (poważnie bez kszty sarkazmu). Sam złapałem się na większości punktów przypominając sobie własne błędy popełnione i popełniane w firmie.


Neal Ford - Evolutionary Architectures


ThoughtWorks buduje swój techniczny wizerunek poprzez "kronikarzy". Fowler jest jednim z nich, ale Neal zdradził, że nowe osoby pracują nad książką będącą almanahem architektonciznym.

W pewnej części pracujemy nad tym w firmie - zestaw metryk i wytycznych architektonicznych. Wiele osób pyta o takie materiały podczas szkoleń i po prezentacjach, wytyczne przydają się również podczas audytów.

Dobrze, że TW oficjalnie rekomenduje bounded context z DDD jako wsparcie w określaniu granicy Microservisów (bez tego całość nie ma sensu i jest skazana na porażkę).

Wydaje mi się, że w wywodzie Neala brakuje jednego elementu, który mówi jak konkretnie podejść do decouplingu: https://en.wikipedia.org/wiki/Connascence_(computer_programming)
Podejście, o którym mówiłem na tegorocznej Confiturze i podlinkuję jak tylko pojawi się video.



Ted Neward - Pragmatic Architecture




Głos sumienia architektów - chało by się rzec:)
Bardzo podoba mi się definicja architektury jaką podaje Ted:
Architektura to zestaw odpowiedzi jakie dajemy zanim programiści je zadadzą. Zasady jakie go prowadzą w codziennej pracy a nie prostokąty.

Osobny problem to: jak te odpowiedzi dokumentować? Swoją propozycję również przedstawię we wspomnianej prezentacji z Confitury.

Kolejna ciekawa myśl to metafora architekta. W IT potrzeba lidera. Ted proponuje metaforę dyrygenta lub w przypadku małego zespołu frontmana, ew. reżysera.




Łukasz Szydło - Preconditions for good code review


Krótka prezentacja, która rzuca świeże światło na sensowne CR, które dają realną zmianę.




Bartek Nowakowski, Kuba Marchwicki - Niańczenie programistów vs. zarządzanie dziećmi

Flow może jeszcze do dopracowania, ale urzekła mnie paralela, którą budują prelegenci - niespodzianka  na samym końcu.

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

A tutaj moja skromna prezentacja:


Prezentacje i wystąpienia - jak nie tylko przetrwać ale i dobrze się bawić

Przy okazji dziękuję wszystkim, którzy głosowali za miejsce w pierwszej dziesiątce wśród tak znakomitego grona. Dzięki!:)

Slajdy - gdyby ktoś potrzebował.

poniedziałek, 28 grudnia 2015

Generowanie diagramów architektonicznych w C4

Jestem wielkim fanem podejścia C4 w dokumentowaniu architektury - co widać na video w poprzednim poście:)

C4 sprawdza się nie tylko dla nowych projektów oraz do nadawania struktury myślom podczas szkoleń... przydaje się również jako "mapa" podczas misji ratunkowych w projektach ze spuścizną:)

Polecam świetną prezentację twórcy C4 na temat narzędzi do automatycznego generowania diagramów. Moim zdaniem generowane nie mają takiej wartości w opowiadaniu historii jak ułożone ręcznie, ale lepsze taki niż żadne aby przedzierać się przez spuściznę:)

Prezentacje jest wartościowa z uwagi na przemyślane komentarze autora z meta-poziomu: kiedy, dlaczego i po co warto stosować dany rodzaj informacji wizualnej oraz rysowanie czego nie ma żadnej wartości:)

https://www.youtube.com/watch?v=oDpdaXt0HQI


//=================
W dużych projektach dodaję M (jak Moduł) tworząc Context Container Module Component Class
gdzie moduł grupuje komponenty.
Moduł można sobie wyobrazić jako jednostkę logiczną (Bounded Context z DDD) czy jako produkt (np. moduł magazynowy czy sprzedaży w ERP) a komponent jako np jednostkę deploymentu (np. klienty, AI itd).
Traktowanie komponentu dosłownie jako @Component w Springu (jak pokazuje przykładowo Brown) wydaje mi się skrajnie niepraktyczne poza malutkimi systemami, ale każdy może sobie popróbować sam co ma dla niego wartość...

piątek, 6 listopada 2015

Architektura dla średnio zaawansowanych programistów

Na początku tego roku stworzyłem prezentację, która podsumowuje kilka ładnych lat mojej pracy i zamyka tym samym pewien etap: "Co było pierwsze: kod czy architektura?"

Została zarejestrowana podczas tegorocznej edycji http://dotnetconf.pl/ (również: Devoxx, Infoshare i kilka mniejszych) i możecie obejrzeć ją w całości poniżej:


Slajdy:




Prezentacja z uwagi na brak czasu omawia wyłanianie się kodu z architektury. Natomiast podejście Emergent Design gdzie architektura wyłania się z kodu można zobaczyć tutaj począwszy od 50. minuty (w nieco gorszej jakości audio i video):





Wiele osób pytało o materiały, więc pomyślałem, że warto zrobić moją prywatną listę książek, które polecam na początek:

  • S. Brown Software architecture for developers - prosta i skuteczna metodyka rysowania diagramów, które mają sens i wnoszą wartość do projektu; dzięki tej książce dowiecie się, że słowo architektura nie ma sensu w IT jeżeli nie dodasz do niego przymiotnika
  • V. Vernon Implementing DDD - nawet jeżeli nie interesujesz się DDD, to znajdziesz tutaj kompendium wiedzy o współczesnych stylach arch w kontekście nietrywialnego przykładu dziedzinowego
  • C. Larman Applying UML and Patterns - tytuł jest zmyłką;) książka nie traktuje o UML, została napisana w czasach gdy UML dodawał +20 do sprzedaży;) autor przedstawia kompletny zwinny proces produkcji softu 
  • Seria Pattern oriented software architecture - to po prostu trzeba mieć w domu na półce... początkowe tomy są dla  wszystkich, końcowe są zorientowane na specyficzne problemy
Swego czasu popełniłem również serię artykułów dla programistamag.pl Receptury projektowe - niezbędnik początkującego architekta, którą można pobrać całkowicie darmowo tutaj: http://bottega.com.pl/artykuly-i-prezentacje#receptury

Tutaj można znaleźć projekt, który ilustruje omawiane techniki https://github.com/BottegaIT/ddd-leaven-v2

Polecam również Udi Dahan.



Mam do Was prośbę: podajcie w komentarzach swoje propozycje "must read" wraz z krótkim komentarzem dlaczego warto.

wtorek, 13 maja 2014

Microservices w kontekście DDD

Mamy nowy buzowrd: mokroserwis, który zdążył być już w typowy da naszej branży - Montypythonowy - sposób wypaczony:)

Autor artykułu Microservices: Usage Is More Important than Size stawia retoryczne pytanie:
czy mikroserwis to
- SOA, które wreszcie doczekało się poprawnej interpretacji (bez ESB i smutnego SOAP)
- czy może stara "dobra" CORBA i RMI tylko, że podana na REST wraz ze wszystkimi smutnymi konsekwencjami zbytniego rozproszenia?

Kolejne figury retoryczne pojawiają się w tekście Microservices? What about Nanoservices?: Sto linijek to jeszcze makro-serwis czy już nano-serwis (funkcjonalność a nie serwis), który jest antywzorcem?

Mierzenie linijek kodu od linijki to jak zwykle znak, że "coś się dzieje";)

Patrząc z dystansu widać to co zwykle: onanizm techniczny zamiast zrozumienia jaki problem próbujemy rozwiązać.

Wydaje mi się, że gdyby za definicję zakresu mikroserwisu przyjąć: API dla Bounded Context z DDD, to wszystko stałoby się jasne...

piątek, 7 czerwca 2013

Open/closed principle - zastosowanie na poziomie architektury aplikacji oraz metafory wizualne

"Kod powinien być otwarty na rozbudowę jak kwiat lotosu o świcie i zamknięty na zmiany jak kwiat lotosu o zmierzchu" - jako początkujący projektant natknąłem się kiedyś w jednej z książek na takie "Buddyjskie" wyjaśnienie Open/Closed Principle - jednej z zasad SOLID.

Jednak jak w praktyce zastosować tą zasadę? Czy aplikuje się ona jedynie na poziomie Object Oriented Design czy również na poziomie architektury aplikacyjnej?

W najnowszym artykule "Zarządzenie złożonością przez trójpodział logiki – Open/closed principle w praktyce" opublikowanym na łamach programistamag.pl przedstawiłem swoje przemyślenia na temat OCP, w których integruję:

  • ODD, 
  • podstawy podejścia funkcyjnego, 
  • Building Blocks wchodzące w skład Domain Driven Design,
  • architekturę na poziomie aplikacyjnym.


W artykule chciałbym zaproponować Wam swego rodzaju "framework mentalny", który pozwala zmierzyć się ze złożonymi problemami dzieląc logikę na 3 kategorie:

  • stabilną - której kod relatywnie rzadko podlega zmianom
  • domknięcia logiki - które nie polegają zmianom a rozbudowie
  • wybór domknięć - zmiany enkapsulowane w fabrykach (zgodnie z regułą Uncle Boba: "instrukcje switch są dozwolone jedynie w czeluściach fabryk":)

Zatem jest to meta-model, który pozwala na tworzenie modeli problemów zgodnych z OCP. W artykule znajdziecie również propozycję 3 rodzajów wizualizacji graficznej OCP: jedno, dwu i trójwymiarową - w zależności od preferencji kognitywnych...

Artykuł do pobrania (jak zwykle całkowicie za darmo) tutaj: http://bottega.com.pl/artykuly-i-prezentacje

wtorek, 30 kwietnia 2013

C4 - Zwinne podejście do odkrywania i dokumentowania architektury

W jaki sposób dokumentować architekturę systemu - z jednej strony tak, aby zawrzeć wszystkie potrzebne informacje, z drugiej zaś, aby nie przeładować dokumentacji szczegółami, które czynią ją bezużyteczną?

Pracując z zespołami podczas warsztatów architektonicznych rozpoczynam zwykle od "zadania na rozgrzewkę", którego treść jest krótka: "wyobraźcie sobie, że od jutra dołączam do Waszego teamu; narysujcie proszę architekturę systemu, nad którym aktualnie pracujecie aby szybko i efektywnie wprowadzić mnie w kontekst oraz abym wiedział  gdzie 'włożyć ręce'".

Jak widać treść nie doprecyzowuje o jaki rodzaj architektury chodzi (aplikacyjna, systemowa, wdrożeniowa, etc) oraz w jaki sposób należy to zrobić - po prostu "tak jak robicie to aktualnie w projekcie", a jeżeli tego typu aktywności nie są podejmowane, "intuicyjnie, jak byś to zrobił/zrobiła".


/*************************
Wiem, że zewnętrzny doradca, który chce 'włożyć ręce' w projekt to niespotykany okaz. Większość z nich to osobniki typu krokodyl - małe rączki, duża gęba (czytaj: nic nie robi, dużo gada).


**************************/

Typowe problemy, jakie pojawiają się podczas prób wizualizowania architektury oraz jeden ze sposobów radzenia sobie z takim krytycznym zadaniem jakim jest komunikowanie swoich pomysłów i strategicznych decyzji opisałem w najnowszym numerze programistamag.

Artykuł C4 - Zwinne podejście do odkrywania i dokumentowania architektury jak zwykle do pobrania (jak zwykle free) tutaj: Niezbędnik początkującego projektanta i architekta (zachęcam oczywiście do odwiedzenia empiku lub zakupu wersji elektrycznej całego numeru).


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

Zainteresowanych zgłębieniem tematu odsyłam do książki Software Architecture for Developers oraz artykułu, który kilka dni temu ukazał się na infoq.

Ze doświadczenia swego oraz zespołów, z którymi pracuję mogę podzielić się jednym rozszerzeniem do C4:  mianowicie w większych systemach Komponenty warto grupować w Moduły (czyli: Context Containers Modules Components Classes). Wewnątrz modułu komponenty "widzą" się wzajemnie, natomiast poza modułem jest widoczny jedynie podzbiór API np. w formie Fasady lub emitowanych zdarzeń (część zdarzeń, które pojawiają się wewnątrz modułu).

środa, 29 sierpnia 2012

Diabeł tkwi w przerywanych liniach

Do czego prowadzi praca w paranoi polegającej na projektowaniu oczywistych oczywistości z pominięciem istotnych szczegółów: The Pragmatic Architect - To Boldly Go Where No One Has Gone Before

Autor w przykładach rozwiązań nawiązujących do jednego z Bulding Blocków DDD: Value Objects, spojrzał zaledwie przez dziurkę od klucza na wierzchołek góry lodowej. Więcej można znaleźć np. tu (w cenie, która w dniu publikacji posta jest bardzo atrakcyjna:).

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

Pierwszy komentarz pod artykułem jest wart takiej samej uwagi na sam artykuł.

wtorek, 10 sierpnia 2010

Pomożecie?


Dzisiejszy post jest nietypowy, ponieważ zwracam się w nim do szanownego grona czytelników z prośbą o pomoc/przysługę. Zostałem zaproszony na tegoroczną konferencję JDD w roli prelegenta i zastanawiam się nad tematem, który mógłby Was potencjalnie (nawet jeżeli nie wybieracie się na konferencję, po prostu ogólnie) zainteresować.

Prezentacja poświęcona Software Craftsmanship i Wzorcom już osiągnęła limit reużywalności, dlatego zastanawiam się nad nowym tematem:) Jeżeli macie jakieś sugestie lub konkretne życzenia odnośnie tego o czym chcielibyście usłyszeć to piszcie śmiało w komentarzu lub na priv (adres w panelu "o mnie" po prawej stronie).

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

Patrząc na ogólne trendy, które w tym roku widać również na polskich konferencjach, można zaobserwować, że raczej odchodzi się od zachwalania kolejnych tuzinów frameworków webowych w stronę zagadnień bardziej ogólnych, zwykle syntetyzując przekrój kilku aspektów. Ciekaw jestem co myślicie o tym trendzie.

Póki co mam pewien pomysł na prezentację poświęconą podejściom do Inversion of Control. Tak, wiem, że temat jest oklepany ale co powiecie na przegląd architektur aplikacji opartych o IoC. Przegląd będący swego rodzaju wycieczką po rozwiązaniach architektonicznych.

Najpierw zastanowilibyśmy się czym w ogóle jest architektura, jakie są jej rodzaje i czym arch. różni się od designu. Później wycieczka po kolejnych podejściach do architektur opartych o 3 techniki IoC w kolejności ich "siły".

Czyli najpierw "najsłabsze" (w sensie siły a nie w sensie, że jest kiepskie) podejście: wstrzykiwanie zależności - co i kiedy warto wstrzykiwać. Bo wstrzykiwanie DAO/Repo do Servisów jest nudne.

Dalej technika "mocniejsza" - bo abstrahująca nie tylko od typu współpracownika, ale i ich ilości oraz czasu - arch. zorientowana na zdarzenia. Przykłady wykorzystania zdarzeń do zwiększania przepustowości oraz do notowania "wydarzeń" biznesowych - wszystko w kontekście event sourcing i Command-query Responsibility Segregation.

Na zakończenie najsilniejsza technika IoC: Aspect Oriented Programming.

Na każdym "przystanku" naszej wycieczki zastanowilibyśmy się co wynika z zastosowania danego podejście i kiedy warto z niego skorzystać - a kiedy jest to tylko moda i owczy pęd.

Jak widać z opisu prezentacja była by przeznaczona dla początkujących architektów aplikacji - programistów, którzy chcieliby zając się projektowaniem w skali makro.

wtorek, 25 maja 2010

Object Oriented czy jedynie Class Oriented?

Dziś będzie o "nowym" podejściu do Object Oriented.
Jeżeli komuś wydaje się, że już nic nowego nie można w tym jakże oklepanym temacie dodać, to zachęcam do zapoznania się prezentacjami zawartymi w tekście. Dowiecie się również dlaczego nowym napisałem w cudzysłowie.

Od jakiegoś czasu z wielu stron pojawiają się zachęty do zainteresowania się nowymi językami, takimi jak Scala, Groovy, Ruby,...

Do tej pory wszyscy orędownicy pokazują jak to fajnie można sobie operować na kolekcjach (sortować, wyszukiwać) w jednej linijce kodu dzięki możliwości "doklejania" nowych metod do istniejących klas. Z całym szacunkiem, ale jest to nic innego jak objaw onanizmu technicznego. Jeżeli mam zainteresować się nowym językiem tylko po to aby uniknąć klepania pętelek to jednak zastosuję "cwanego" utilsa. Czy aby na pewno nowe języki powstają tylko po to aby sobie dokleić do klasy String 100 nowych metodek, o których istnieniu wie tylko doklejający?

Jeszcze inni odkryli ponownie możliwość dynamicznego dodawania "w locie" do encji metod zapisujących i wczytujących je. Myślałem, że niesławny Active Record odszedł już dawno w niepamięć. Ale jednak nie - okazuje się, że jest sexi w niektórych frameworkach.

Podczas niedawnej sesji researchu trafiłem na ciekawą koncepcję, która nadaje prawdziwy i pragmatyczny sens tym wszystkim konstruktom językowym.Pojawił się oto "nowy" paradygmat programowania: Data Context Interaction, który wskazuje zastosowanie dynamicznych konstruktów do lepszego (a przynajmniej innego) modelowania problemów. Czyli mamy coś więcej niż sortowanie listy, mamy nową jakość myślenia o modelowanej strukturze.

Twórcy DCI twierdzą, że mainstreamowe podejście do OO to jedynie kilka % prawdziwego OO. Klasyczne podejście skupia się na klasach, czyli na pewnych strukturach. Stawiają oni klasycznemu podejściu zarzuty, że skupia się ono na klasach zamiast na obiektach, co jakoby powoduje "rozsmarowanie" logiki Use Case po wielu klasach, dzięki czemu w nietrywialnych aplikacjach z czasem coraz ciężej przychodzi połapanie się w logice.

Czy zatem czas na powrót do paradygmatu proceduralnego, gdzie mamy cały "flow" w 1 miejscu?
Niekoniecznie.
Spójrzmy ma poniższy rysunek:


źródło: http://www.underbjerg.com/2009/11/16/oredev-2009-impressions-and-dci-architecture/

Dane opisują przy pomocy Klas pewną niezmienną strukturę - można powiedzieć core modelu (może mieć on pewne bazowe, ogólne zachowania).
Interakcje są wyniesieniem zachowania do poziomu głównych bytów, dzięki czemu lepiej odpowiadają modelowi mentalnemu usera. Są pewnymi rolami, które mogą być przyjęte przez obiekty. Interakcje operują na Danych ponieważ są do nich mixowane w...
Kontekście - kontekst odpwiada Use Caseom lub ich krokom. To w pewnym kontekście dane są łączone z rolami (zachowaniem) tworząc dopiero obiekty.

Intuicyjny przykład: jestem człowiekiem, opisuje mnie zestaw standardowych parametrów. Ale w zależności od kontekstu przyjmuję niektóre role (a wraz z nimi zachowania): Programista, Manager, Ojciec, Kierowca, Gracz, Klient, ...


Jeżeli udało mi się chociaż trochę zainteresować Was koncepcją DCI (nie mylić z nieco mniej innowacyjnym CDI;) to polecam następujące materiały:

- Architektura i ciekawy przykład w Ruby - aby zaintrygować dodam, że znajdziecie tutaj ostrą krytykę TDD.
- Ciekawe podejście do modelowania - mamy tutaj przykład niesamowitej jasności myślenia i pokaz tragikomicznego frameworka Qi4j (Java się jednak nie nadaje do DCI)
- Niezbyt porywające wprowadzenie teoretyczne - z którego dowiemy się, że te koncepcje mają już kilkadziesiąt lat, tylko gdzieś się zapodziały w przemyśle.
- Niekoniecznie świadome DCI w Scali - plus durne przykłady sortowania kolekcji;)


Ogólnie polecam materiały z konferencji Øredev - jak widać tematyka jest jest bardzo ciekawa i mocno wykracza poza mainstream.

A w inkubatorze (póki co) Eclipse mamy coś bardzo podobnego: Object Teams


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

Ciekawie DCI podsumował jeden ze znajomych architektów (przy okazji "snajper", który potrafi trafić człowieka z granatnika z odległości całej planszy w Modern Warfare 1): "ale trzeba mieć jaja aby zakwestionować standardy i przedstawić coś tak odmiennego".

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ą.

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

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

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

piątek, 25 lipca 2008

Z pamiętnika zdomenowanego modelarza: #2 Architektura

- Ogry są... jak cebula!
- Śmierdzą?!
- Tak... nie! ... Ogry mają WARSTWY!

Jak już pisałem pracowałem ostatnio nad architekturą dla nowego systemu. Wymagania niefunkcjonalne sprawiły, że przybrała ona kształt niezbyt wyrafinowany, jednak z paroma ciekawymi smaczkami. Do tego jest ona zanurzona w Springu i przykryta Seamem (o integracji w następnym odcinku).

Architektura promowana przez Seam to w ogóle jakieś śmieszne nieporozumienie rodem z programów zaliczeniowych na studiach;)
Z tego powodu, jak i z innych, o których napiszę później Seam został potraktowany jako framework prezentacji dostarczający paru fajnych ficzerów; za obsługę persystencji dziękujemy.

Architektura nie jest jeszcze dopracowana w szczegółach i pewne rzeczy mogą się zmienić ale z grubsza wygląda to tak...
Oczywiście mamy warstwy:
- warstwa prezentacji - klasyczne backing beany przykryte JSF, właściwie to faceletami, właściwie to RichFaces. Seamowe akcje i model odpadają ponieważ zakładam bardzo rozbudowaną logikę prezentacji czyli modyfikowanie, przestawianie, przełączanie i zmienianie komponentów JSF. Zatem potrzebuję odrębnej warstwy na tego typu rzeźbę...
- warstwa logiki aplikacji - aplikacja będzie głównie "UseCase oriented" więc w tym miejscu pojawią się klasy odpowiadające konkretnym juzkejsom. Taki UC zazwyczaj składa się z wielu kroków (metod), zatem przydałoby się przechowywać jego stan w sesji. No ale zasięg sesji to obciach - lepiej nadaje się do tego Seamowy zasięg konwersacji (przynajmniej do czegoś przyda się ten badziewiasty framework;)
Oczywiście nie wszystko będzie juzkejsem. Pojawią się tu również usługi aplikacyjne, które z powodu niemocy twórczej tymczasowo nazwałem sobie Fasadami (chociaż nie podoba mi się ta nazwa).

Warstwa logiki aplikacji będzie stanowić granicę systemu - ponad nią znajduje się po prostu jakaś mniej lub bardziej wyrafinowana powłoka (oczywiście najważniejsza z punktu widzenia marketingowego - ludzie uwielbiają gówno w kolorowym papierku).
Oprócz logicznego podziału na kroki juzkejsów będzie ona zawierać całą logikę, którą nie zaliczamy do logiki biznesowej (bo ta jest piętro niżej). Zatem znajdą się tu wszelkiego rodzaju podsystemy powiadomień, interakcji z systemem i innymi systemami itp.

- warstwa logiki domenowej - tu mieszka Domain Driven Design czyli serce naszego systemu. Większość klas z tej warstwy modeluje naszą dziedzinę. Będzie tu na przykład Pacjent, Oddział, Lek, itp. W DDD mamy kilka rodzajów "artefaktów" biznesowych: Encje, ValueObjects, Servisy, Fabryki, Agregaty - długo by o tym pisać, dlatego ta warstwa będzie tematem następnego posta.

- warstwa DAO - tak, tak... oldskulowe DAO. Co prawda z jego funkcjonalnością będzie pretendować do miana Repozytorium, ale nie będziemy się wydurniać z tą nazwą.


Typowy przepływ wygląda tak:
1. Akcja użytkownika rozpoczyna use case
2. Use case jest inicjalizowany i umieszczany w konwersacji Seama.
3. Kolejne akcje użytkownika odpalają metody odpowiadające krokom UC, gdzie dla każdego kroku zazwyczaj:
4. UC używa DAO aby pobrać Encje (ewentualnie tworzy agregaty encji przy pomocy fabryk)
5. UC stymuluje;) encje do wchodzenia w reakcje między sobą oraz wykonywania logiki biznesowej
6. UC może ewentualnie posiłkować się servisami biznesowymi (ustawiając im DAO), które zazwyczaj produkują ValueObjects
7. UC zazwyczaj w ostatnim kroku dokonuje utrwalenia encji przy pomocy DAO
Ogólnie to Kolejne kroki UC zmieniają jego stan, który jest jakoś prezentowany na GUI.


Tak pokrótce wyglądają warstwy. Jak widać architektura różni się od klasycznej proceduralnej-quasi-obiektowej architektury jedynie paroma konwencjami, a jednak intuicja podpowiada mi, że będzie to lepiej pracować i odsunie o kilkanaście miesięcy moment zwiększenia entropii do poziomu popularnie zwanego syfem w kodzie.
Różnie cą następujące:
- Stan juzkejsu w jednym miejscu zamiast w wielu backing beanach.
- Logika nie jest rozsiana pomiędzy procedury.
- Model domenowy nie jest anemiczny - jego klasy posiadają odpowiedzialność, możemy zagrać w nim wzorcami projektowymi w celu zmniejszenia komplikacji (chyba, że ktoś ma pecha i nie zna wzorców - wówczas komplikacja pozornie rośnie;)
- Paczki procedur ograniczone do minimum - servisy biznesowe


//==============================
W kolejnych postach opiszę bardziej szczegółowo każdą warstwę wraz z przykładową implementacją oraz przesłanki, które doprowadziły do niektórych strategicznych decyzji - niektóre z decyzji są dosyć kontrowersyjne, np: rezygnacja z lazy loading, uzywanie detached objects.
Przedstawię też integrację Seam ze Spring