Kuba, Łukasz, Piotrek i Paweł - trenerzy, z którymi współpracuję pod marką Bottgea - udostępnili swe prezentacje, zapraszam:
W kategorii Inżynieria oprogramowania:
- Jakub Kubryński (ekspert JVM i tuning) - Spring Boot – framework for micro services
- Łukasz Szydło (ekspert Java i tools) - Enabling Continuous Delivery
- Piotr Karczmarz (ekspert .NET i arch.) - MVVM pattern in cross-platform architecture based on Xamarin
W kategorii Tematyka miękka:
- Paweł Badeński (ex ThoughtWorks, człowiek Agile) - Feedback
Inżynieria oprogramowania w ujęciu systemowym.
Zintegrowane podejście do metodyk,
technologii (głównie Java EE), architektury i rozwoju ścieżki kariery programisty.
Pokazywanie postów oznaczonych etykietą Spring. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą Spring. Pokaż wszystkie posty
środa, 16 kwietnia 2014
wtorek, 30 sierpnia 2011
Zaczyn DDD+CqRS
Jak pewnie zauważyliście, częstotliwość publikowania postów spadła ostatnio dosyć znacznie. A to za sprawą mocnego zaangażowania w nowy projekt - projekt ilustrujący techniki modelowania Domain Driven Design oraz wybrane podejścia do implementacji architektury Command-query Responsibility Segreation.
Projekt osiągnął poziom wersji Beta pierwszego Milestonea, tak więc już oficjalnie możemy go upublicznić: http://code.google.com/p/ddd-cqrs-sample/.
Zaczyn?
Nazwa ociekająca folklorem jest ilustracją idei: projekt to coś więcej niż Sample ale zdecydowanie nie jest to kolejny Framework - więcej na ten temat na wiki.
Gwoli wyjaśnienia: zaczyn to coś z czego powstaje chleb.
Odeszliśmy od formy frameworka przekonani przez samego Grega Younga (twórcę CqRS i propagatora DDD). Greg przekonał mnie podczas prywatnych rozmów, że framework dla CqRS delikatnie mówiąc nie ma sensu:)
Zawartość projektu odpowiada na pytania techniczne, które pojawiają się tak często, że uznaliśmy, że czas stworzyć sampla ilustrującego nasze podejście. Do tej pory istnieją dwa takie Sample napisane w Javie - my jednak chcieliśmy pójść nieco dalej. Tak więc projekt jest sumą doświadczeń jego twórców.
Co w nim znajdziecie:
- ilustracja wszystkich Builiding Blocków DDD (bez uproszczeń technicznych)
- ilustracja zaawansowany technik DDD (Bounded Context to nie są akademickie rozważania)
- ewolucyjne podejście do architektury warstwowej, różne poziomy separacji CqRS (na tym etapie nie wprowadzamy Event Sourcingu - wyjaśnienie powodów na wiki)
- pragmatyczne podejście do CqRS
- część bardzo specyficznej wiedzy wyniesioną przez nas z wiosennego szkolenia Grega Younga
- modelowanie czasu poprzez zdarzenia i dosyć nową technikę modelowania czasu: Sagi
- kilka eleganckich trików z wykorzystaniem Springa i Hibernate:)
- techniki testowania: jednostkowe, integracyjne i akceptacyjne
- połączenie JBehave (Behavior Driven Development) z Selenium
- już niedługo: nasz własny pomysł na uogólnienie historyjek tak by wspierały zarówno testowanie przez GUI jak i przez API
Zainteresowanych zapraszamy do:
- wiki - kilkadziesiąt stron A4 plus trzy wizualizacje w Prezi
- SVN - aktualnie ok 150 klas platformy zaczynu oraz przykładów DDD
- grupy dyskusyjnej
- bloga
//====================================
Teksty techniczne z zakresu DDD i CqRS będę publikował na nowym blogu. Natomiast niniejszy będzie wciąż prowadzony jako miejsce na tematy pozostałe.
Projekt osiągnął poziom wersji Beta pierwszego Milestonea, tak więc już oficjalnie możemy go upublicznić: http://code.google.com/p/ddd-cqrs-sample/.
Zaczyn?
Nazwa ociekająca folklorem jest ilustracją idei: projekt to coś więcej niż Sample ale zdecydowanie nie jest to kolejny Framework - więcej na ten temat na wiki.
Gwoli wyjaśnienia: zaczyn to coś z czego powstaje chleb.
Odeszliśmy od formy frameworka przekonani przez samego Grega Younga (twórcę CqRS i propagatora DDD). Greg przekonał mnie podczas prywatnych rozmów, że framework dla CqRS delikatnie mówiąc nie ma sensu:)
Zawartość projektu odpowiada na pytania techniczne, które pojawiają się tak często, że uznaliśmy, że czas stworzyć sampla ilustrującego nasze podejście. Do tej pory istnieją dwa takie Sample napisane w Javie - my jednak chcieliśmy pójść nieco dalej. Tak więc projekt jest sumą doświadczeń jego twórców.
Co w nim znajdziecie:
- ilustracja wszystkich Builiding Blocków DDD (bez uproszczeń technicznych)
- ilustracja zaawansowany technik DDD (Bounded Context to nie są akademickie rozważania)
- ewolucyjne podejście do architektury warstwowej, różne poziomy separacji CqRS (na tym etapie nie wprowadzamy Event Sourcingu - wyjaśnienie powodów na wiki)
- pragmatyczne podejście do CqRS
- część bardzo specyficznej wiedzy wyniesioną przez nas z wiosennego szkolenia Grega Younga
- modelowanie czasu poprzez zdarzenia i dosyć nową technikę modelowania czasu: Sagi
- kilka eleganckich trików z wykorzystaniem Springa i Hibernate:)
- techniki testowania: jednostkowe, integracyjne i akceptacyjne
- połączenie JBehave (Behavior Driven Development) z Selenium
- już niedługo: nasz własny pomysł na uogólnienie historyjek tak by wspierały zarówno testowanie przez GUI jak i przez API
Zainteresowanych zapraszamy do:
- wiki - kilkadziesiąt stron A4 plus trzy wizualizacje w Prezi
- SVN - aktualnie ok 150 klas platformy zaczynu oraz przykładów DDD
- grupy dyskusyjnej
- bloga
//====================================
Teksty techniczne z zakresu DDD i CqRS będę publikował na nowym blogu. Natomiast niniejszy będzie wciąż prowadzony jako miejsce na tematy pozostałe.
środa, 21 kwietnia 2010
Google Application Engine

Dziś będzie o rzetelności.
WYGLĄDA OBIECUJĄCO
Chmura Google - GAE ogólnie prezentuje się świetnie. Za darmo (do pewnych limitów) dostajemy platformę, która jest w stanie udźwignąć tysiące transakcji na sekundę oraz przechować dla nas terabajty danych w nierelacyjnej bazie BigTable. Do tego dostęp do wielu usług google takich jak bezpieczeństwo, składowanie obrazków, pobieranie zawartości stron poprzez ichni cache, potężny memcache itp.
Programować możemy sobie w Pythonie albo w Javie (okrojonej wersji, bez części klas standardowych i z zakazem korzystania z pewnych bibliotek).
Ogólne założenie jest takie: programiści (i konsultanci;) w większości nie potrafią konfigurować skalowalnych środowisk ani pisać skalowalnych aplikacji, dlatego dostarczmy im platformę, która zajmie się tym problemem w sposób transparentny. Czyli mamy podejście odwrotne do Java EE. Zajmij się problemem biznesowym a chmura zajmie się skalowaniem. Kierunek wydaje się słuszny.
Zatem zaczynamy zabawę...
Na dzień dobry krótka, oficjalna prezentacja z głównej strony jak to fajnie i szybko klepie się aplikacje i odpala je w kosmos: http://www.youtube.com/watch?v=P3GT4-m_6RQ
Trzeba przyznać, że plugin do Eclipse jest wzorowy: prosty w obsłudze (3 sugestywne buttony), działa oraz zapewnia wszystko:
- lokalny server emulujący chmurę i jej zasoby (bazę)
- narzędzie automatycznie "wzbogacające" bytecode klas encji na potrzeby ichniej "implementacji" JPA
- narzędzie wystrzeliwujące aplikację w kosmos
- prosty kreator, który generuje cały ten kosmodrom
(nie odważyłem się zmawenizować wygenerowanego projektu, bo to zwykle tydzień w plecy a na koniec może się okazać, że tracimy hotdeploy)
WTF?
Ale, ale... akcja filmu rozkręca się z czasem...
Jedna z największych firm programistycznych na ziemi pokazuje jak tworzyć kod w... Java Server Pages. Patrzcie uważnie i uczcie się: logika i dostęp do danych w... scriptletach (sic!).
Oczywiście duzi chłopcy wiedzą, ze scriptlety są depricated. A nóż widelec tą scenę zobaczy jakieś małe dziecko i będzie później tak programować jak dorośnie?!? Później znowu przeczytamy w gazecie o tragedii jaka rozegrała się w jakimś spokojnym projekcie. Gdzie byli rodzice?!?
Hmmm dziwne, dziwne... no ale nie czepiajmy się szczegółów, to przecież tylko taki przykład. Przecież zawsze możemy zaprojektować sobie eleganckie warstwy i całość oprzeć o sprawdzony framework.
BOLESNE OGRANICZENIA
Niestety tak jak wspomniałem na początku JVM ma ograniczenia i nie wszystko działa. Seam co prawda daje się uruchomić po zastosowaniu 15 speszyl haków, ale wszyscy twierdzą, że nie warto ryzykować. Na szczęście stary, dobry Spring oficjalnie działa. Wielu dzielnych blogerów publikuje hello worldy i tutoriale jak to poskładać. Wygląda na to, że jedni kopiują od drugich;)
Ba, nawet 2 książki o GAE preferują Springa i pokazują jak go skonfigurować. Hmmm ciekawe dlaczego omawiają tylko servlecik Spring MVC i dyplomatycznie nie poruszają bardziej zaawansowanych zagdanień...?
Niestety, jednak nie działa do końca!
Przykładowo gdy użyjemy technik Aspektowych i przeplatania w czasie uruchomienia to mamy dziwne błędy ClassNotFound. Lokalnie działa, a na chmurze już nie.
Co implikuje, że możemy zapomnieć o aspektowej, eleganckiej obsłudze bezpieczeństwa i transakcji.
Więc po co nam Spring? Tylko do wstrzykiwania? To bez sensu.
UWAGA NA PERSYSTENCJĘ
Nieważne, bez Springa można żyć, idźmy dalej... Nierelacyjna baza BigTable została przez Google przykryta interfejsem JDO albo JPA (do wyboru) więc czujemy się jak w domu.
Nie do końca... specyfikacja ma szereg ograniczeń wynikających z architektury rozwiązania BigTable - chodzi o rozpraszanie struktur danych na różne maszyny po stronie chmury.
Drobny szczególik wspomniany jedynie w dokumentacji jest taki, ze agregaty encji są grupowane w rodziny. Przykładowo jeżeli mamy encję Zamówienie, która ma w sobie listę ZamówionychProduktów to jest to rodzina. Co z tego wynika?
Encja może należeć jedynie do jednej rodziny.
Implikacja: Jeżeli mamy encje Zamówienie i Faktura oraz encję Kleint, który musi być podpięty do Zamówienia jak i Faktury to niestety nie ma takiej możliwości. Dziecko może mieć jednego rodzica. Oczywiście można to obejść i w Zamówieniu jak i Fakturze używać obiektów Kluczy do Klienta. Ale wówczas tracimy łatwość programowania i uzależniamy się od API Chmury.
Trzeba pamiętać, że podejście do modelowania danych w nierelacyjnej bazie wymaga innego "nastawienia" - taka natura rozwiązania. Jeżeli nierelacyjnie to może obiektowo? Jednak nie do końca - no to jak?
Osobiście preferuję takie podejście, że Zamówienie czy Faktura ma owszem klucz do Klienta ale ma również niepersystentne pole Klient, które jest ustawiane w niej osobnym zapytaniem (nie jest to duży koszt w bazie BigTable, która jest tak na prawdę wielowymiarową HashMapą). Tego typu szczegóły można hermetyzować z obiektach Repository, które zwracają dane "poskładane" tak aby było wygodnie. OK, jeden problem z głowy.
Transakcja może obejmować tylko operacje na jednej rodzinie.
Czyli jeżeli logika aplikacji chce zapisać zarówno rodzinkę Zamówienie jak i Faktura to będą to 2 osobne transakcje. A co jeżeli w tak zwanym międzyczasie zdarzy się coś przykrego? Ręczna rzeźba i sprzątanie:)
Poza tym, jak wspomniałem, aspektowe transakcje Springa nie działają, więc brudzimy kod biznesowy kodem API transakcji JPA.
Datanucleus - "implementacja" JPA
Oprócz tego, że jest okrojona to dodatkowo zawiera szereg błędów - nie działają nawet niektóre proste przykłady z tutoriali JPA. Na oficjalnej grupie wisi wiele pytań - bez odpowiedzi.
GWÓŹDŹ
Prezentacja
Z frameworków prezentacji testowałem JSF.
1.2 (z Seam) działa.
2.0 działa po zastosowaniu jednego prostego speszyl haka.
Działa w sensie, że się uruchamia. W 2.0 występują pewne problemy z wstrzykiwaniem ManagedBeanów. RichFaces ani IceFaces nie mają wersji dla chmury. Oficjalnie Primefaces częściowo działa. Znowu nie jest to rzetelna i informacja - AJAX ogólnie się sypie.
No i można zapomnieć o uruchamianiu metod z parametrem przy pomocy nowego silnika wyrażeń (el-api w wersji 2.2). Chmura odmawia współpracy z nowymi zdobyczami.
//==============================
Biorąc pod uwagę powyższe ograniczenia:
- brak wygodnego frameworka (w tym wsparcia dla AOP)
- uboga funkcjonalność ORM wymagająca wielu ręcznych operacji
- mentalny model programowania sprzed o ok 10 lat
Mamy znaczny spadek produktywności.
Domyślamy się skąd te JSP, scriptlety i statyczne metody w filmie promocyjnym? Czyżby tylko to działało?
Szkoda tylko, że community i blogosfera zatrzymuje się na poziomie Hello World i nie weryfikuje nieco bardziej dogłębnie i rzetelnie.
Na zakończenie dodam, że zupełnie inne założenia przy budowie chmury przyjął Amazon. Dostajemy sprzęt i system operacyjny. Nikt nie narzuca nam dodatkowych bytów zbudowanych nad sprzętem i nie wchodzi nam z butami w nasz wypracowany latami i sprawdzony model programowania. I co najważniejsze - wszystko działa:)
środa, 5 listopada 2008
Wprowadzenie do wstrzykiwania zależności i Springa zarazem
Dzisiaj krótki kurs wstrzykiwania zależności w Springu (i nie tylko). Kurs powstał na bazie prezentacji, którą przedstawiłem w ramach lubelskiego JUG.
Prezentacja była skierowana z założenia do początkujących, jednak aby nie zanudzić bardziej zaawansowanych zawiera ona również parę zagadnień dla nich - zostały one oznaczone czterolistną koniczynką (żeby pozostać w wiosennym klimacie). Zatem jeżeli jesteś początkujący i w pewnym momencie nie rozumiesz o co chodzi a na slajdzie widnieje koniczynka wówczas wszystko jest ok.
Celem posta jest przedstawienie ogólnej idei Inversion of Control (IoC) oraz jej szczególnej reifikacji (trudne słowo) zwanej Dependency Injection (DI). Jest to wg mnie bardzo istotne na początek ponieważ duża część nowszych frameworków zasadza się na tych fundamentach. Później możemy przejść do konkretnego przykładu w Springu. Przeglądając różne tutoriale stwierdzam brak wyjaśnienia o co w ogóle chodzi z IoC czy DI. Pokazuje się jedynie jakieś autystyczne przykładziki bez wyjaśnienia sedna problemu - po co i na co mam coś wstrzykiwać, co mi to daje i kiedy powinienem to robić.
Ludzie podobno najlepiej uczą się na przykładach. Owszem - do nauki prostych czynności typu rozbicie kokosa kamieniem wystarczy sam przykład; jednak jak przedstawić ideę na samym przykładzie? Albo inaczej: jaka ilość przykładów pozwoli na drodze logicznego myślenia wyłonić z nich ideę?
Zaczynamy.

Zgodnie z agendą przejdziemy po niektórych slajdach...

Na początek wymyślimy sobie problem. Problem oczywiście będzie polegał na tym, że pojawiają się zależności i nie wiemy jak sobie z nimi poradzić. Później nastąpi krótki przegląd możliwych rozwiązań. Wszystkie one są oczywiście złe:) Dalej całkiem przypadkiem wpadniemy na koncepcję rozwiązania dużo lepszego. Poznamy ogólną koncepcję IoC i DI. Teraz jesteśmy już gotowi na zapoznanie się z przykładową implementacją w Springu. Następnie przedstawię zarówno zalety jak i ograniczenia wstrzykiwania zależności. Na zakończenie poszerzymy nieco horyzonty... Spring to nie tylko wstrzykiwanie zależności.
KONTEKST PROBLEMU
Na potrzeby naszych rozważań próbowałem wymyślić jakiś ekscytujący problem biznesowy. Niestety bezskutecznie. Dlatego musimy się zadowolić wyświechtanym przykładem z zamówieniem. Zatem załóżmy, że mamy zamówienie, które musimy złożyć. Nasz proces składania zamówienie niech będzie trywialny: wyliczamy podatek i zapisujemy zamówienie.
Zakładam, że kombinujemy w stronę Object Oriented oraz, że chcemy zaprojektować nasze rozwiązanie zgodnie z GRASP.

Zgodnie ze standardowym (choć nie koniecznie najlepszym) tokiem myślenia powinniśmy dość do powyższego projektu. W centrum mamy encję biznesową Order. Nie chcemy aby była ona anemicznym modelem więc przykładowo przypisujemy jej metodę biznesową wliczającą wartość tegoż zamówienia. Dla przejrzystości pomijamy szczegóły takie jak atrybuty zamówienia (data, status itp) oraz jego wewnętrzną strukturę (kolekcję produktów) ponieważ nie wnoszą one nic w kontekście wstrzykiwania zależności.
Logika aplikacji (logika przepływu, use case, ogólnie - nie biznesowa) jest zhermetyzowana w serwisie. Logika ta jest prosta, polega na wyliczeniu podatku i zapisaniu zamówienia. Ponieważ chcemy podążać zgodnie z OO to aspekt podatku i zapisu został wydzielony (enkapsulacja). Oba aspekty mogą mieć różną postać dlatego zostały przykryte stabilnym interfejsem.
PROBLEM
Ok, nasza logika aplikacji zależy od 2 rzeczy: polityka wyliczania podatku i sposób zapisu zamówienia. Pytanie: skąd je wziąć?

Niektórzy się tym nie przejmują i radośnie hardcodują typy w kodzie. Tzn usztywnianie kodu następuje dużo wcześniej. Po co w ogóle wydzielać jakieś klasy:P
Gdy jednak projektant przyciśnie i mamy już hermetyzację oraz wymaganie co do możliwości zmiany implementacji pojawia się pomysł jej wyboru przy pomocy kaskady ifów albo słicza.
Dalej nauczeni doświadczeniem z problemami słiczy zmian w setkach miejsc enkapsulujemy je do jakiejś fabryczki. Koncepcja ta może wyewoluować nawet do całkiem zgrabnego rozwiązania opartego o Abstract Factory, który zaczytuje konfigurację np z XML.
Jako uzupełnienie wspomnę tylko rozwiązanie, które widziałem u sprytnego mistrza .NET: dyrektywy kompilacji - bez komentarza.
O ile rozwiązanie oparte o fabrykę abstrakcyjną jest już całkiem w porządku to ma jednak wadę estetyczną. Kod biznesowy czy jakikolwiek inny jest zbrukany jakimiś dziwnymi odwołaniami do technicznych obiektów fabryk. Fuj.
Skoro odrzucamy myśl o tym, że obiekt zajmuje się zdobywaniem swych zależności to nie pozostaje nic innego jak zapodawanie ich z zewnątrz.

POMYSŁ NA ROZWIĄZANIE
Jeżeli obiekt nie jest odpowiedzialny za tworzenie czy zdobywanie swych zależności lecz dostaje z kątowni to właśnie odkryliśmy ideę Inversion of Control!


W standardowym podejściu nasz kod korzysta z już istniejących bibliotek (nawet gdyby była to klasa napisane minutę temu) w celu nadbudowania nad nimi nowej struktury. Nasz kod krok po kroku wywołuje biblioteki. W IoC jest odwrotnie - to kod biblioteczny wywołuje nasz własny kod - wymaga to zmiany sposobu myślenia. Nie programujemy imperatywnie lecz deklaratywnie aby coś zbudować. Konstrukcja już istnieje lecz brakuje w niej pewnych szczególnych komponentów realizujących szczegółowe funkcje. Można to sobie wyobrazić w ten sposób, że kod biblioteki zajmuje się kompleksowym rozwiązaniem problemu jednak w pewnym miejscach woła kod klienta. To jaki kod klienta biblioteki ma być podłączony musimy właśnie w jakiś sposób zadeklarować (podłączony - chciało by się powiedzieć wstrzyknięty, ale nie uprzedzajmy faktów).
W naszym przykładzie servis składający zamówienie jest powiedzmy biblioteką. Załóżmy, że ktoś stworzył doskonałą bibliotekę do składnia zamówień, ale oczywiście nie wiedział jak zaimplementować wyliczanie podatku i zapis zamówienia. Dlatego zostawił to w gestii kodu klienta biblioteki. Jeszcze raz podkreślę różnicę: kod klienta nie woła biblioteki - jest na odwrót. Jest to podejście charakterystyczne dla frameworków.
Ogólnie można powiedzieć, że IoC jest podejściem do projektowania architektur.
PRÓBA ROZWIĄZANIA W NOWYM STYLU
Skoro nasz pomysł z odwróceniem kontroli nie jest objawem schorzenia, ponieważ jest opisany nawet na wiki, to spróbujmy może tak:

Skoro nasz servis potrzebuje załadowania go zależnościami przed rozpoczęciem pracy to wychodzi chyba na to, że składanie powinno nastąpić gdzieś w interfejsie użytkownika.

Nic bardziej mylnego!
UI służy do różnych dziwnych rzeczy ale na pewno nie powinno zajmować się decydowaniem o składaniu komponentów biznesowych. Wiem, że kiedyś w onClick pisało się żywego SQLa, ale czasy Visual Basica czy Delphi już się skończyły. Co prawda niechlubną tradycję podtrzymuje Seam, ale normalnie warstwa prezentacji służy do hermetyzowania logiki prezentacji (ot niespodzianka) - czyli np decydowania czy z uwagi na imieniny usera narysować kwiatek czy butelkę whisky.
POTRZEBUJEMY WSTRZYKIWANIA ZALEŻNOŚCI

Idea wstrzykiwania zależności jest prosta jak budowa cepa. Musi istnieć jakiś byt (zwany zazwyczaj kontenerem IoC), który ma dostęp do konfiguracji i bibliotek klas. Byt ten poproszony o jakiś obiekt szuka jego definicji w konfiguracji. Na podstawie konfiguracji jest w stanie określić jakiej klasy powinien być żądany obiekt oraz jakie ma zależności. Zależne obiekty są w razie potrzeby tworzony po czym są wstrzyknięte (po prostu wstawione) do obiektu żądanego. Oczywiście same obiekty zależne mogą same mieć własne zależności - kontener również dla nich przeprowadzi rekursywnie opisaną procedurę.
Uwagę na powyższym slajdzie przykuwa zapewne pękaty słoik. Istotne jest, że kontener pracuje na POJOs. Czyli DI nie wymaga uzależniania naszego kodu od jakiś dziwnych interfejsów. Nie wymaga dziedziczenia po ezoterycznych klasach bazowych. Kod jest czysty ponieważ zajmuje się tym czym powinien.
WYJAŚNIENIE
Zanim przejdziemy do przykładu, na który już pewnie wszyscy czekają z niecierpliwością, chcę wyjaśnić jedną kwestię...

Pojęcia IoC i DI są nagminnie używane zamiennie. IoC i DI nie są synonimami. Di jest jednym ze sposobów na osiągniecie IoC. Owszem najczęściej realizujemy IoC przez DI ale to nie znaczy, że można je utożsamiać.
PRZYKŁAD WSTRZYKIWANIA ZALEŻNOŚCI PRZEZ SPRING IoC
Na potrzeby przykładu zaimplementowałem projekt przedstawiony wcześniej w UML. Struktura pakietów odpowiada standardowym warstwom, więc mamy kolejno (wg"wysokości" a nie wg sortowania przez drzewko pakietów w Eclipse):

Przyjrzyjmy się bliżej klasie OrderServiceImpl z pakietu logiki aplikacji. Jej trywialna metoda biznesowa makeOrder jedyne co robi to skorzystanie z polityki podatkowej w celu wyliczenia podatku i z DAO w celu zapisania zamówienia.
Metoda ta korzysta z prywatnych pól.
Skąd wezmą się ich wartości? Oczywiście zostaną wstrzyknięte przez kontener IoC Springa. W jaki sposób? Poprzez settery:
Zwróćmy uwagę iż settery występują jedynie w klasie implementacji i nie ma ich w interfejsie. Po prostu to ta konkretne implementacja serwisu potrzebuje do swego sensownego działa tych 2 zależności. Być może inne implementacje nie koniecznie muszą mieć takie same zależności. Dlatego nie zakładajmy nić o zależnościach i nie umieszczajmy setterów w interfejsach.
Cały dramat zaczyna się właśnie w klasie UI nazwanej dumnie ConsoleApplication.
I to wszystko. Kolejne linijki odpowiadają za:
Łatwe, proste i przyjemne.
Gdzie cała magia? Oczywiście w konfiguracji Springa - plik application-context.xml (Przy okazji dodam, że wygodne może być rozbicie konfiguracji na kilka plików; np osobny plik dla każdej z warstw).
CO MISIO MA W ŚRODKU...
Składania XMLa konfiguracyjnego dla Springa jest dosyć intuicyjna.
Przeanalizujmy plik konfiguracyjny od dołu. Deklarujemy obiekt o nazwie xmlOrderDAO podając konkretną jego klasę. Wyżej podobnie deklarujemy obiekt polishTaxPolicy. Jednak akurat ta polityka zależy od warośći VAT, którą potrzebuje w swym konstruktorze - wstrzyknięcie do konstruktora deklarujemy w tagu constructor-arg.
Na końcu (a właściwie początku) deklarujemy obiekt orderServiceBean - ten sam, który pobieramy z BeanFactory. Obiekt ten zależy od 2 poprzednich - ma nawet settery, które tylko czekają aby im wstrzyknąć jakieś implementacje interfejsów. Wstrzyknięcie poprzez setter deklarujemy z użyciem tagu property.
Spring "widząc" taką konfigurację "wie" czym jest obiekt orderServiceBean (żądany w aplikacji z fabryki) i wie również jak wstrzyknąć jego zależności, które oczywiście same mogą mieć również własne zależności.
INTEGRACJA CAŁKOWITA
Powyższy przykład ma jedną wadę. Najwyższa warstwa aplikacji korzysta z kontekstu Springa. Owszem klasy niższych warstw nie zajmują się już pozyskiwaniem swych zależności, ale niestety nie uniknęliśmy tego w warstwie UI.
Nie wiem jak wygląda to w aplikacjach standalone, ale Spring gładko integruje się z frameworkami webowymi.

W przypadku czystego JSF czy Seam wystarcz w web.xml zadeklarować listenera, który "podniesie" kontekst Springa (wskazując mu ewentualnie lokalizację plików konfiguracyjnych). Później już tylko ustawienie specjalnego resolvera w faces-config.xml i o tej pory w wyrażeniach EL możemy się posługiwać obiektami z kontekstu Springa. W przypadku integracji z Seam możemy nawet deklarować zasięg beanów Springowych tak samo jak Seamowych. Więcej o integracji Seam ze Spring tu.
Ogólnie nie mamy już tej niezręcznej sytuacji polegającej na tym, że najwyższa warstwa jednak zdobywa zależności we własnym zakresie.
SINGLETONY
Domyślnie wszystkie beany w Springu są singletonami (attrybut scope tagu bean). Zmiana jego wartości na prototype skutkuje tym, że wstrzyknięcie referencji spowoduje wstrzyknięcie całkiem nowej instancji.

OGRANICZENIA WSTRZYKIWANIA ZALEŻNOŚCI W SPRINGU (UWAGA - KONICZYNKA)
W odróżnieniu od nowszych frameworków (np Seam) Spring wstrzykuje zależności podczas tworzenia obiektów. Co jeżeli Spring nie będzie odpowiedzialny za stworzenie obiektu - tak jak np w wypadku encji tworzonych przez frameworki ORM? Jeżeli Spring czegoś nie tworzy, to nie może temu czemuś wstrzyknąć zależności - logiczne.

Pewnie niejeden czytelnik zadaje sobie w tym momencie pytanie: o czym on do cholery pisze? Wstrzykiwanie zależności do encji z hibernate? Otóż z godnie z Domain Driven Design encje powinny mieć odpowiedzialność biznesową. W końcu to obiekty a nie jakieś recordy z Pascala albo struct z C.
Jednym ze sposobów na wstrzyknięcie zależności do encji jest Springowa adnotacja @Configurable. Niestety polega ona na weavingu bytecodu podczas ładowania więc praktycznie wyklucza to testowalność.
Zalecanym patentem są interceptory Hibernate. Interceptor może po stworzeniu encji ustawić jej to i owo.
Czyli oficjalne wersja jest taka: do encji wstrzykujemy zależności poprzez interceptory hibernate a wszystko inne jest nakłuwane Springiem. Moim skromnym zdaniem dwa sposoby na wstrzykiwanie zależności to co najmniej o jeden za dużo! Sugeruję aby zamiast rzeźbić po prostu pozbyć się problemu:)

Zamiast wstrzykiwać do encji sugerują aby wstrzykiwać do warstwy wyższej - logiki aplikacji. Scenariusz wyglądał by tak:
1. Coś (np polityka) jest wstrzyknięta do serwisu aplikacyjnego
2. Serwis przy pomocy DAO/Repozytorium pobiera encję
3. Serwis po prostu ustawia zależność na pobranej właśnie encji
Dzięki temu przy tak zwanej okazji dostajemy za darmo użyteczne narzędzie. Możemy z dużym prawdopodobieństwem założyć, że w różnych use case, czyli różnych serwisach aplikacyjnych będą potrzebne np różne polityki. W sugerowanym podejściu nie stanowi to żadnego problemu. Do jednego servisu wstrzykujemy politykę A, do drugiego B. Servis gdy trzeba ustawi swoją (wstrzykniętą) politykę do encji. Natomiast w razie gdybyśmy uparcie dążyli do wstrzykiwania zależności (np polityk) wprost do encji wówczas byłby niemały problem.
ZALETY PŁYNĄCE Z IoC i DI
Inversion of Control skłania do projektowania systemów za zasadzie konfigurowalnych (a co za tym idzie reużywalnych) komponentów. Posługując się Dependency Injection możemy dostarczać klientom różne wersje systemów, które technicznie różnią się jedynie plikami konfiguracyjnymi Springa.
Pamiętajmy jednak, że sam fakt korzystania z kontenera IoC nie zapewnia nam eleganckiej i reużywalnej architektury. Wszystko zależy od ilości i jakości pracy umysłowej włozonej w projekt. DI to jedynie narzędzie do realizacji naszej dobrej lub błędnej koncepcji.
Projektowanie systemu na zasadzie niezależnych komponentów drastycznie zwiększa testability. Idąc dalej możemy przygotowywać specjalne wersje plików konfiguracyjnych spreparowana na potrzeby testów integracyjnych. Na ten przykład w razie zależności od modułu komunikującego się z zewnętrznym systemem możemy na czas testów wstrzyknąć implementację tego modułu, która jedynie symuluje interakcję. Przydaje się to na pewno gdy interakcja jest długotrwała a my chcemy przeprowadzić testy masowe. Oczywiście kontener IoC nie powinien być angażowany do testów jednostkowych.

No więc właśnie...
Wstrzykiwanie zależności to jedynie fundament,na którym stoi framework. Zresztą zobaczmy na architekturę Springa

Oraz na architekturę pełnej aplikacji

Mam nadzieję, że powyższa architektura wygląda zachęcająco do dalszego zgłębiania Springa...
Jak widać Spring stawia na integrację dobrych rozwiązań zamiast na implementację wszystkiego od nowa.
Chociaż gdyby stale nie wymyślano koła od nowa to jeździli byśmy wciąż na drewnianych krążkach.

//========================
Prezentacja do ściągnięcia tu: open office i mikrosoft ofis. Natomiast przykłady zawarte zostały w prostym projekciku Eclipse. Projekcik ma strukturę źródeł zgodną z Maven ale sam nie jest projektem Maven - biblioteki są po prostu skopiowane do katalogu lib.
EDIT
video: część 1, część 2
Prezentacja była skierowana z założenia do początkujących, jednak aby nie zanudzić bardziej zaawansowanych zawiera ona również parę zagadnień dla nich - zostały one oznaczone czterolistną koniczynką (żeby pozostać w wiosennym klimacie). Zatem jeżeli jesteś początkujący i w pewnym momencie nie rozumiesz o co chodzi a na slajdzie widnieje koniczynka wówczas wszystko jest ok.
Celem posta jest przedstawienie ogólnej idei Inversion of Control (IoC) oraz jej szczególnej reifikacji (trudne słowo) zwanej Dependency Injection (DI). Jest to wg mnie bardzo istotne na początek ponieważ duża część nowszych frameworków zasadza się na tych fundamentach. Później możemy przejść do konkretnego przykładu w Springu. Przeglądając różne tutoriale stwierdzam brak wyjaśnienia o co w ogóle chodzi z IoC czy DI. Pokazuje się jedynie jakieś autystyczne przykładziki bez wyjaśnienia sedna problemu - po co i na co mam coś wstrzykiwać, co mi to daje i kiedy powinienem to robić.
Ludzie podobno najlepiej uczą się na przykładach. Owszem - do nauki prostych czynności typu rozbicie kokosa kamieniem wystarczy sam przykład; jednak jak przedstawić ideę na samym przykładzie? Albo inaczej: jaka ilość przykładów pozwoli na drodze logicznego myślenia wyłonić z nich ideę?
Zaczynamy.
Zgodnie z agendą przejdziemy po niektórych slajdach...

Na początek wymyślimy sobie problem. Problem oczywiście będzie polegał na tym, że pojawiają się zależności i nie wiemy jak sobie z nimi poradzić. Później nastąpi krótki przegląd możliwych rozwiązań. Wszystkie one są oczywiście złe:) Dalej całkiem przypadkiem wpadniemy na koncepcję rozwiązania dużo lepszego. Poznamy ogólną koncepcję IoC i DI. Teraz jesteśmy już gotowi na zapoznanie się z przykładową implementacją w Springu. Następnie przedstawię zarówno zalety jak i ograniczenia wstrzykiwania zależności. Na zakończenie poszerzymy nieco horyzonty... Spring to nie tylko wstrzykiwanie zależności.
KONTEKST PROBLEMU
Na potrzeby naszych rozważań próbowałem wymyślić jakiś ekscytujący problem biznesowy. Niestety bezskutecznie. Dlatego musimy się zadowolić wyświechtanym przykładem z zamówieniem. Zatem załóżmy, że mamy zamówienie, które musimy złożyć. Nasz proces składania zamówienie niech będzie trywialny: wyliczamy podatek i zapisujemy zamówienie.
Zakładam, że kombinujemy w stronę Object Oriented oraz, że chcemy zaprojektować nasze rozwiązanie zgodnie z GRASP.

Zgodnie ze standardowym (choć nie koniecznie najlepszym) tokiem myślenia powinniśmy dość do powyższego projektu. W centrum mamy encję biznesową Order. Nie chcemy aby była ona anemicznym modelem więc przykładowo przypisujemy jej metodę biznesową wliczającą wartość tegoż zamówienia. Dla przejrzystości pomijamy szczegóły takie jak atrybuty zamówienia (data, status itp) oraz jego wewnętrzną strukturę (kolekcję produktów) ponieważ nie wnoszą one nic w kontekście wstrzykiwania zależności.
Logika aplikacji (logika przepływu, use case, ogólnie - nie biznesowa) jest zhermetyzowana w serwisie. Logika ta jest prosta, polega na wyliczeniu podatku i zapisaniu zamówienia. Ponieważ chcemy podążać zgodnie z OO to aspekt podatku i zapisu został wydzielony (enkapsulacja). Oba aspekty mogą mieć różną postać dlatego zostały przykryte stabilnym interfejsem.
PROBLEM
Ok, nasza logika aplikacji zależy od 2 rzeczy: polityka wyliczania podatku i sposób zapisu zamówienia. Pytanie: skąd je wziąć?
Niektórzy się tym nie przejmują i radośnie hardcodują typy w kodzie. Tzn usztywnianie kodu następuje dużo wcześniej. Po co w ogóle wydzielać jakieś klasy:P
Gdy jednak projektant przyciśnie i mamy już hermetyzację oraz wymaganie co do możliwości zmiany implementacji pojawia się pomysł jej wyboru przy pomocy kaskady ifów albo słicza.
Dalej nauczeni doświadczeniem z problemami słiczy zmian w setkach miejsc enkapsulujemy je do jakiejś fabryczki. Koncepcja ta może wyewoluować nawet do całkiem zgrabnego rozwiązania opartego o Abstract Factory, który zaczytuje konfigurację np z XML.
Jako uzupełnienie wspomnę tylko rozwiązanie, które widziałem u sprytnego mistrza .NET: dyrektywy kompilacji - bez komentarza.
O ile rozwiązanie oparte o fabrykę abstrakcyjną jest już całkiem w porządku to ma jednak wadę estetyczną. Kod biznesowy czy jakikolwiek inny jest zbrukany jakimiś dziwnymi odwołaniami do technicznych obiektów fabryk. Fuj.
Skoro odrzucamy myśl o tym, że obiekt zajmuje się zdobywaniem swych zależności to nie pozostaje nic innego jak zapodawanie ich z zewnątrz.
POMYSŁ NA ROZWIĄZANIE
Jeżeli obiekt nie jest odpowiedzialny za tworzenie czy zdobywanie swych zależności lecz dostaje z kątowni to właśnie odkryliśmy ideę Inversion of Control!
W standardowym podejściu nasz kod korzysta z już istniejących bibliotek (nawet gdyby była to klasa napisane minutę temu) w celu nadbudowania nad nimi nowej struktury. Nasz kod krok po kroku wywołuje biblioteki. W IoC jest odwrotnie - to kod biblioteczny wywołuje nasz własny kod - wymaga to zmiany sposobu myślenia. Nie programujemy imperatywnie lecz deklaratywnie aby coś zbudować. Konstrukcja już istnieje lecz brakuje w niej pewnych szczególnych komponentów realizujących szczegółowe funkcje. Można to sobie wyobrazić w ten sposób, że kod biblioteki zajmuje się kompleksowym rozwiązaniem problemu jednak w pewnym miejscach woła kod klienta. To jaki kod klienta biblioteki ma być podłączony musimy właśnie w jakiś sposób zadeklarować (podłączony - chciało by się powiedzieć wstrzyknięty, ale nie uprzedzajmy faktów).
W naszym przykładzie servis składający zamówienie jest powiedzmy biblioteką. Załóżmy, że ktoś stworzył doskonałą bibliotekę do składnia zamówień, ale oczywiście nie wiedział jak zaimplementować wyliczanie podatku i zapis zamówienia. Dlatego zostawił to w gestii kodu klienta biblioteki. Jeszcze raz podkreślę różnicę: kod klienta nie woła biblioteki - jest na odwrót. Jest to podejście charakterystyczne dla frameworków.
Ogólnie można powiedzieć, że IoC jest podejściem do projektowania architektur.
PRÓBA ROZWIĄZANIA W NOWYM STYLU
Skoro nasz pomysł z odwróceniem kontroli nie jest objawem schorzenia, ponieważ jest opisany nawet na wiki, to spróbujmy może tak:
Skoro nasz servis potrzebuje załadowania go zależnościami przed rozpoczęciem pracy to wychodzi chyba na to, że składanie powinno nastąpić gdzieś w interfejsie użytkownika.
Nic bardziej mylnego!
UI służy do różnych dziwnych rzeczy ale na pewno nie powinno zajmować się decydowaniem o składaniu komponentów biznesowych. Wiem, że kiedyś w onClick pisało się żywego SQLa, ale czasy Visual Basica czy Delphi już się skończyły. Co prawda niechlubną tradycję podtrzymuje Seam, ale normalnie warstwa prezentacji służy do hermetyzowania logiki prezentacji (ot niespodzianka) - czyli np decydowania czy z uwagi na imieniny usera narysować kwiatek czy butelkę whisky.
POTRZEBUJEMY WSTRZYKIWANIA ZALEŻNOŚCI
Idea wstrzykiwania zależności jest prosta jak budowa cepa. Musi istnieć jakiś byt (zwany zazwyczaj kontenerem IoC), który ma dostęp do konfiguracji i bibliotek klas. Byt ten poproszony o jakiś obiekt szuka jego definicji w konfiguracji. Na podstawie konfiguracji jest w stanie określić jakiej klasy powinien być żądany obiekt oraz jakie ma zależności. Zależne obiekty są w razie potrzeby tworzony po czym są wstrzyknięte (po prostu wstawione) do obiektu żądanego. Oczywiście same obiekty zależne mogą same mieć własne zależności - kontener również dla nich przeprowadzi rekursywnie opisaną procedurę.
Uwagę na powyższym slajdzie przykuwa zapewne pękaty słoik. Istotne jest, że kontener pracuje na POJOs. Czyli DI nie wymaga uzależniania naszego kodu od jakiś dziwnych interfejsów. Nie wymaga dziedziczenia po ezoterycznych klasach bazowych. Kod jest czysty ponieważ zajmuje się tym czym powinien.
WYJAŚNIENIE
Zanim przejdziemy do przykładu, na który już pewnie wszyscy czekają z niecierpliwością, chcę wyjaśnić jedną kwestię...
Pojęcia IoC i DI są nagminnie używane zamiennie. IoC i DI nie są synonimami. Di jest jednym ze sposobów na osiągniecie IoC. Owszem najczęściej realizujemy IoC przez DI ale to nie znaczy, że można je utożsamiać.
PRZYKŁAD WSTRZYKIWANIA ZALEŻNOŚCI PRZEZ SPRING IoC
Na potrzeby przykładu zaimplementowałem projekt przedstawiony wcześniej w UML. Struktura pakietów odpowiada standardowym warstwom, więc mamy kolejno (wg"wysokości" a nie wg sortowania przez drzewko pakietów w Eclipse):

- ui - interfejs użytkownika. Żałosny ten przykładowy interfejs, ale działa;P
- logic.application - zawiera logikę naszej apliakacji, czyli konkretny Use Case - usługę składania zamówienia (jest to podejście proceduralne, ale co tam - na potrzeby tego przykładu wystarczające)
- logic.business - model biznesu, mamy tu dwie standardowe figury z Domain Driven Desing: Encję oraz Politykę. Polityki są przykryte stabilnym interfejsem ponieważ zakładamy, że są polimorficzne. Polityka to nic innego mój ulubiony wzorzec projektowy - Strategy.
- dao - zawiera interfejs OrderDAO i jego przykładową implementację opartą na XML
Przyjrzyjmy się bliżej klasie OrderServiceImpl z pakietu logiki aplikacji. Jej trywialna metoda biznesowa makeOrder jedyne co robi to skorzystanie z polityki podatkowej w celu wyliczenia podatku i z DAO w celu zapisania zamówienia.
@Override
public void makeOrder(Order order) {
taxPolicy.calculateTax(order);
orderDAO.saveOrder(order);
}
Metoda ta korzysta z prywatnych pól.
private OrderDAO orderDAO;
private TaxPolicy taxPolicy;
Skąd wezmą się ich wartości? Oczywiście zostaną wstrzyknięte przez kontener IoC Springa. W jaki sposób? Poprzez settery:
public void setOrderDAO(OrderDAO orderDAO) {
this.orderDAO = orderDAO;
}
public void setTaxPolicy(TaxPolicy taxPolicy) {
this.taxPolicy = taxPolicy;
}
Zwróćmy uwagę iż settery występują jedynie w klasie implementacji i nie ma ich w interfejsie. Po prostu to ta konkretne implementacja serwisu potrzebuje do swego sensownego działa tych 2 zależności. Być może inne implementacje nie koniecznie muszą mieć takie same zależności. Dlatego nie zakładajmy nić o zależnościach i nie umieszczajmy setterów w interfejsach.
Cały dramat zaczyna się właśnie w klasie UI nazwanej dumnie ConsoleApplication.
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{"application-context.xml"});
BeanFactory factory = context;
OrderService orderService = (OrderService) factory.getBean("orderServiceBean");
orderService.makeOrder(new Order());
I to wszystko. Kolejne linijki odpowiadają za:
- Utworzenie kontekstu Spring. W tym przypadku tworzymy go używające klasy
org.springframework.context.support.ClassPathXmlApplicationContext.ClassPathXmlApplicationContext zajmuje się ona wyszukaniem podanego pliku konfiguracyjnego (który powinien znajdować się na class path). Plik ten zawiera definicję wszystkich zależności; zostanie omówiony za chwilę. Kontekst podczas "wstawania" zaczytuje konfigurację i ewentualnie tworzy obiekty wraz z ich zależnościami. - Rzutowanie kontekstu na fabrykę to jedynie lukier i dobra praktyka. Kontekst posiada wiele metod "technicznych", które nie są potrzebne "zwykłym" kawałkom kodu. Do pobierania obiektów z kontenera wystarczy nam interfejs BeanFactory.
- Pobranie z kontekstu obiektu o zadanej nazwie. Fabryka zwraca Object więc potrzeba rzutowania. W tym miejscu interesuje mnie jedynie interfejs obiektu; z resztą nie mam nawet pojęcia jaka konkretna klasa może siedzieć w konfiguracji.
- Pozostało nam już tylko wykonanie metody biznesowej na pobranym z kontenera obiekcie.
Łatwe, proste i przyjemne.
Gdzie cała magia? Oczywiście w konfiguracji Springa - plik application-context.xml (Przy okazji dodam, że wygodne może być rozbicie konfiguracji na kilka plików; np osobny plik dla każdej z warstw).
CO MISIO MA W ŚRODKU...
Składania XMLa konfiguracyjnego dla Springa jest dosyć intuicyjna.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="orderServiceBean" class="org.slawek.project1.logic.application.impl.OrderServiceImpl">
<property name="taxPolicy"><ref bean="polishTaxPolicy"/></property>
<property name="orderDAO" ref="xmlOrderDAO"/>
</bean>
<bean id="polishTaxPolicy" class="org.slawek.project1.logic.business.policies.impl.PolishTaxPolicy">
<constructor-arg type="int" value="22"/>
</bean>
<bean id="xmlOrderDAO" class="org.slawek.project1.dao.xml.XMLOrderDAO"/>
</beans>
Przeanalizujmy plik konfiguracyjny od dołu. Deklarujemy obiekt o nazwie xmlOrderDAO podając konkretną jego klasę. Wyżej podobnie deklarujemy obiekt polishTaxPolicy. Jednak akurat ta polityka zależy od warośći VAT, którą potrzebuje w swym konstruktorze - wstrzyknięcie do konstruktora deklarujemy w tagu constructor-arg.
Na końcu (a właściwie początku) deklarujemy obiekt orderServiceBean - ten sam, który pobieramy z BeanFactory. Obiekt ten zależy od 2 poprzednich - ma nawet settery, które tylko czekają aby im wstrzyknąć jakieś implementacje interfejsów. Wstrzyknięcie poprzez setter deklarujemy z użyciem tagu property.
Spring "widząc" taką konfigurację "wie" czym jest obiekt orderServiceBean (żądany w aplikacji z fabryki) i wie również jak wstrzyknąć jego zależności, które oczywiście same mogą mieć również własne zależności.
INTEGRACJA CAŁKOWITA
Powyższy przykład ma jedną wadę. Najwyższa warstwa aplikacji korzysta z kontekstu Springa. Owszem klasy niższych warstw nie zajmują się już pozyskiwaniem swych zależności, ale niestety nie uniknęliśmy tego w warstwie UI.
Nie wiem jak wygląda to w aplikacjach standalone, ale Spring gładko integruje się z frameworkami webowymi.

W przypadku czystego JSF czy Seam wystarcz w web.xml zadeklarować listenera, który "podniesie" kontekst Springa (wskazując mu ewentualnie lokalizację plików konfiguracyjnych). Później już tylko ustawienie specjalnego resolvera w faces-config.xml i o tej pory w wyrażeniach EL możemy się posługiwać obiektami z kontekstu Springa. W przypadku integracji z Seam możemy nawet deklarować zasięg beanów Springowych tak samo jak Seamowych. Więcej o integracji Seam ze Spring tu.
Ogólnie nie mamy już tej niezręcznej sytuacji polegającej na tym, że najwyższa warstwa jednak zdobywa zależności we własnym zakresie.
SINGLETONY
Domyślnie wszystkie beany w Springu są singletonami (attrybut scope tagu bean). Zmiana jego wartości na prototype skutkuje tym, że wstrzyknięcie referencji spowoduje wstrzyknięcie całkiem nowej instancji.

OGRANICZENIA WSTRZYKIWANIA ZALEŻNOŚCI W SPRINGU (UWAGA - KONICZYNKA)
W odróżnieniu od nowszych frameworków (np Seam) Spring wstrzykuje zależności podczas tworzenia obiektów. Co jeżeli Spring nie będzie odpowiedzialny za stworzenie obiektu - tak jak np w wypadku encji tworzonych przez frameworki ORM? Jeżeli Spring czegoś nie tworzy, to nie może temu czemuś wstrzyknąć zależności - logiczne.

Pewnie niejeden czytelnik zadaje sobie w tym momencie pytanie: o czym on do cholery pisze? Wstrzykiwanie zależności do encji z hibernate? Otóż z godnie z Domain Driven Design encje powinny mieć odpowiedzialność biznesową. W końcu to obiekty a nie jakieś recordy z Pascala albo struct z C.
Jednym ze sposobów na wstrzyknięcie zależności do encji jest Springowa adnotacja @Configurable. Niestety polega ona na weavingu bytecodu podczas ładowania więc praktycznie wyklucza to testowalność.
Zalecanym patentem są interceptory Hibernate. Interceptor może po stworzeniu encji ustawić jej to i owo.
Czyli oficjalne wersja jest taka: do encji wstrzykujemy zależności poprzez interceptory hibernate a wszystko inne jest nakłuwane Springiem. Moim skromnym zdaniem dwa sposoby na wstrzykiwanie zależności to co najmniej o jeden za dużo! Sugeruję aby zamiast rzeźbić po prostu pozbyć się problemu:)

Zamiast wstrzykiwać do encji sugerują aby wstrzykiwać do warstwy wyższej - logiki aplikacji. Scenariusz wyglądał by tak:
1. Coś (np polityka) jest wstrzyknięta do serwisu aplikacyjnego
2. Serwis przy pomocy DAO/Repozytorium pobiera encję
3. Serwis po prostu ustawia zależność na pobranej właśnie encji
Dzięki temu przy tak zwanej okazji dostajemy za darmo użyteczne narzędzie. Możemy z dużym prawdopodobieństwem założyć, że w różnych use case, czyli różnych serwisach aplikacyjnych będą potrzebne np różne polityki. W sugerowanym podejściu nie stanowi to żadnego problemu. Do jednego servisu wstrzykujemy politykę A, do drugiego B. Servis gdy trzeba ustawi swoją (wstrzykniętą) politykę do encji. Natomiast w razie gdybyśmy uparcie dążyli do wstrzykiwania zależności (np polityk) wprost do encji wówczas byłby niemały problem.
ZALETY PŁYNĄCE Z IoC i DI
Inversion of Control skłania do projektowania systemów za zasadzie konfigurowalnych (a co za tym idzie reużywalnych) komponentów. Posługując się Dependency Injection możemy dostarczać klientom różne wersje systemów, które technicznie różnią się jedynie plikami konfiguracyjnymi Springa.
Pamiętajmy jednak, że sam fakt korzystania z kontenera IoC nie zapewnia nam eleganckiej i reużywalnej architektury. Wszystko zależy od ilości i jakości pracy umysłowej włozonej w projekt. DI to jedynie narzędzie do realizacji naszej dobrej lub błędnej koncepcji.
Projektowanie systemu na zasadzie niezależnych komponentów drastycznie zwiększa testability. Idąc dalej możemy przygotowywać specjalne wersje plików konfiguracyjnych spreparowana na potrzeby testów integracyjnych. Na ten przykład w razie zależności od modułu komunikującego się z zewnętrznym systemem możemy na czas testów wstrzyknąć implementację tego modułu, która jedynie symuluje interakcję. Przydaje się to na pewno gdy interakcja jest długotrwała a my chcemy przeprowadzić testy masowe. Oczywiście kontener IoC nie powinien być angażowany do testów jednostkowych.

No więc właśnie...
Wstrzykiwanie zależności to jedynie fundament,na którym stoi framework. Zresztą zobaczmy na architekturę Springa

Oraz na architekturę pełnej aplikacji

Mam nadzieję, że powyższa architektura wygląda zachęcająco do dalszego zgłębiania Springa...
Jak widać Spring stawia na integrację dobrych rozwiązań zamiast na implementację wszystkiego od nowa.
Chociaż gdyby stale nie wymyślano koła od nowa to jeździli byśmy wciąż na drewnianych krążkach.

//========================
Prezentacja do ściągnięcia tu: open office i mikrosoft ofis. Natomiast przykłady zawarte zostały w prostym projekciku Eclipse. Projekcik ma strukturę źródeł zgodną z Maven ale sam nie jest projektem Maven - biblioteki są po prostu skopiowane do katalogu lib.
EDIT
video: część 1, część 2
niedziela, 19 października 2008
Szkielety, zręby, rusztowania, ruiny
Zbieram sobie spokojnie materiały do prezentacji o Inversion of Control w Springu (w sumie to raczej o Springu:) na potrzeby naszego JUG. Aż tu nagle... prowokujący post Piotrka.
Jako wstęp do pracy nad prezentacją o Springu, dziś będzie o frameworkach w ogóle.
Najpierw należy odróżnić framework od biblioteki. Biblioteka to będzie chyba zbiór reużywalnych i sprawdzonych kawałków kodu skupionych wokół jakiegoś problemu. Nie będę tutaj silił się na definicję, wiadomo o co chodzi (jeżeli ktoś się chce do niej przyczepić to proszę bardzo;P). Ogólnie sytuacja jest taka: masz zestaw klas czy procedur (w wersji oldskulowej) i radź sobie. Składaj je w coś większego.
Natomiast framework ma już swą formę. Jest rozwiązaniem ogólnym, w którym brakuje jedynie szczegółów, które to musimy właśnie zdefiniować. Coś jak wypełnianie szkieletu mięsem. Zazwyczaj zajmuje się powtarzalnymi szczegółami technicznymi - dla nas zostaje biznes. Przynajmniej takie są założenia;)
Jeszcze jedna kategoria: zaznaczam, że nie chcę wyjść na zarozumiałego dupka, który lepiej od twórców Springa wie czym jest Spring, ale wydaje mi się, że jest on biblioteką frameworków a nie frameworkiem.
Komentując posta podlinkowanego przez Piotrka... Rzeczywiście jest tak, że jakaś część programistów podchodzi do frameworków z oporem. Dziwne, bo wydaje mi się, że prędzej czy później każdy jakieś swoje nano-frameworki tworzy. Jest to chyba immanentna cecha programistów, którzy uchwycili w pewnym momencie Object Oriented. Najpierw zaczynasz od uogólnień i abstrakcji. Projekt cieszy swą elegancją i prostotą - cieszy to niestety tylko Ciebie i ew. paru kolegów. Później w razie potrzeby refaktoryzacja do paru wzorców projektowych oraz wyciągnięcie konfiguracji na zewnątrz kodu. Okazuje się, ze konfiguracji jest zbyt wiele (koszmar XML), więc idąc z duchem czasu stawiamy na Convention Over Configuration. I quasi-framework gotowy. Heh hiestety zazwyczaj jego życie trwa jeden projekt bo do innych się nie nadaje... jest zbyt mało ogólny - ale to szczegół.
Z drugiej strony... frameworki budzą niechęć. Z obserwacji wydaje mi się, że przyczyny mogą być różne:
ignorancja - w przypadku niektórych frameworków bez biegłości w OO i wzorcach projektowych może być niestety ciężko połapać się o co chodzi w danym frameworku. Przytłacza on swą pozorną (bo wynikającą z niewiedzy) złożonością. Patrząc bardziej ogólnie problem może polegać na niechęci do zmian i poznawania nowych rzeczy.
pycha - jest jakaś część programistów, która nie wyrosła jeszcze ze śpioszków i twierdzą, że każdą kupę potrafią zrobić sami. Więc radośnie wynajdują np ORM na nowo. Tworzą coś co już zostało napisane zamiast skupić się na rozwiązaniu problemu klienta (biznesowego problemu). Bo ja sam UMIE i nie potrzebuje tutaj niczyjej pomocy a wzorce są dla cieniasów - Nie no fajnie, ale to do cholery kosztuje! Oczywiście niektórzy z czasem dochodzą do poziomu na którym ich rozwiązania są reużywalne - powstają wówczas chałupnicze nano-frameworki domowej roboty. Pycha każe im sądzić, że są one lepsze i mają mniej bugów niż coś co powstało w silnym community. Patrząc bardziej ogólnie: pyszne Zosie Samosie gardzą nie tylko frameworkami ale i oldskulowymi bibliotekami - po prostu wszystkim czego same radośnie nie spłodziły.
lenistwo - lepiej samemu coś napisać niż wertować strony dokumentacji istniejącego frameworka. Do tego jeszcze tony konfiguracji (niestety nasz własny framework jeżeli ma być frameworkiem też będzie musiał być jakoś konfigurowalny:P). Oczywiście na krótką metę lub w małych projektach pisanie z palca może być bardziej opłacalne. Patrząc bardziej ogólnie: co z samorozwojem? Przyjrzenie się architekturze sprawdzonego rozwiązania zazwyczaj otwiera nowe furtki w mózgu - pewne konstrukcje projektowe mogą być na prawdę inspirujące.
Kwestia najważniejsza: tytułowe ruiny. Frameworki (nawet te poważne i "oficjalne") nie zawsze muszą być arcydziełem. Nie koniecznie muszą rzeczywiście wspierać nas w rozwiązaniu problemu. W praktyce mogą przeszkadzać i irytować. Np taki JSF - wydaje się być zaprojektowany przez doskonałych inżynierów oprogramowania, którzy jednak niestety chyba nigdy w życiu nie popełnili żadnej aplikacji webowej (choćby w php).
//===================
Dobry framework powinien być tak zaprojektowany aby można było wymienić w nim nie tylko "mięcho" ale również część niego samego, czyli "szkieletu". Stąd chyba w Springu tyle interfejsów w bardziej jak i mniej newralgicznych miejscach:)
Jako wstęp do pracy nad prezentacją o Springu, dziś będzie o frameworkach w ogóle.
Najpierw należy odróżnić framework od biblioteki. Biblioteka to będzie chyba zbiór reużywalnych i sprawdzonych kawałków kodu skupionych wokół jakiegoś problemu. Nie będę tutaj silił się na definicję, wiadomo o co chodzi (jeżeli ktoś się chce do niej przyczepić to proszę bardzo;P). Ogólnie sytuacja jest taka: masz zestaw klas czy procedur (w wersji oldskulowej) i radź sobie. Składaj je w coś większego.
Natomiast framework ma już swą formę. Jest rozwiązaniem ogólnym, w którym brakuje jedynie szczegółów, które to musimy właśnie zdefiniować. Coś jak wypełnianie szkieletu mięsem. Zazwyczaj zajmuje się powtarzalnymi szczegółami technicznymi - dla nas zostaje biznes. Przynajmniej takie są założenia;)
Jeszcze jedna kategoria: zaznaczam, że nie chcę wyjść na zarozumiałego dupka, który lepiej od twórców Springa wie czym jest Spring, ale wydaje mi się, że jest on biblioteką frameworków a nie frameworkiem.
Komentując posta podlinkowanego przez Piotrka... Rzeczywiście jest tak, że jakaś część programistów podchodzi do frameworków z oporem. Dziwne, bo wydaje mi się, że prędzej czy później każdy jakieś swoje nano-frameworki tworzy. Jest to chyba immanentna cecha programistów, którzy uchwycili w pewnym momencie Object Oriented. Najpierw zaczynasz od uogólnień i abstrakcji. Projekt cieszy swą elegancją i prostotą - cieszy to niestety tylko Ciebie i ew. paru kolegów. Później w razie potrzeby refaktoryzacja do paru wzorców projektowych oraz wyciągnięcie konfiguracji na zewnątrz kodu. Okazuje się, ze konfiguracji jest zbyt wiele (koszmar XML), więc idąc z duchem czasu stawiamy na Convention Over Configuration. I quasi-framework gotowy. Heh hiestety zazwyczaj jego życie trwa jeden projekt bo do innych się nie nadaje... jest zbyt mało ogólny - ale to szczegół.
Z drugiej strony... frameworki budzą niechęć. Z obserwacji wydaje mi się, że przyczyny mogą być różne:
ignorancja - w przypadku niektórych frameworków bez biegłości w OO i wzorcach projektowych może być niestety ciężko połapać się o co chodzi w danym frameworku. Przytłacza on swą pozorną (bo wynikającą z niewiedzy) złożonością. Patrząc bardziej ogólnie problem może polegać na niechęci do zmian i poznawania nowych rzeczy.
pycha - jest jakaś część programistów, która nie wyrosła jeszcze ze śpioszków i twierdzą, że każdą kupę potrafią zrobić sami. Więc radośnie wynajdują np ORM na nowo. Tworzą coś co już zostało napisane zamiast skupić się na rozwiązaniu problemu klienta (biznesowego problemu). Bo ja sam UMIE i nie potrzebuje tutaj niczyjej pomocy a wzorce są dla cieniasów - Nie no fajnie, ale to do cholery kosztuje! Oczywiście niektórzy z czasem dochodzą do poziomu na którym ich rozwiązania są reużywalne - powstają wówczas chałupnicze nano-frameworki domowej roboty. Pycha każe im sądzić, że są one lepsze i mają mniej bugów niż coś co powstało w silnym community. Patrząc bardziej ogólnie: pyszne Zosie Samosie gardzą nie tylko frameworkami ale i oldskulowymi bibliotekami - po prostu wszystkim czego same radośnie nie spłodziły.
lenistwo - lepiej samemu coś napisać niż wertować strony dokumentacji istniejącego frameworka. Do tego jeszcze tony konfiguracji (niestety nasz własny framework jeżeli ma być frameworkiem też będzie musiał być jakoś konfigurowalny:P). Oczywiście na krótką metę lub w małych projektach pisanie z palca może być bardziej opłacalne. Patrząc bardziej ogólnie: co z samorozwojem? Przyjrzenie się architekturze sprawdzonego rozwiązania zazwyczaj otwiera nowe furtki w mózgu - pewne konstrukcje projektowe mogą być na prawdę inspirujące.
Kwestia najważniejsza: tytułowe ruiny. Frameworki (nawet te poważne i "oficjalne") nie zawsze muszą być arcydziełem. Nie koniecznie muszą rzeczywiście wspierać nas w rozwiązaniu problemu. W praktyce mogą przeszkadzać i irytować. Np taki JSF - wydaje się być zaprojektowany przez doskonałych inżynierów oprogramowania, którzy jednak niestety chyba nigdy w życiu nie popełnili żadnej aplikacji webowej (choćby w php).
//===================
Dobry framework powinien być tak zaprojektowany aby można było wymienić w nim nie tylko "mięcho" ale również część niego samego, czyli "szkieletu". Stąd chyba w Springu tyle interfejsów w bardziej jak i mniej newralgicznych miejscach:)
sobota, 26 lipca 2008
Spring+Seam - integracja dla ubogich
Wczoraj opisałem dosyć ogólnie wszystkie warstwy. Teraz pora na obniżenie poziomu abstrakcji; zobaczmy jak to wszystko ze sobą skleić przy pomocy Springa.
Oczywiście można zadać pytanie: po co sie wydurniać? Czy używanie Springa to nie nakładanie gaci przez głowę? Przecież Seam wspiera injekcje, bijeckje, outjekcje...
Założenie jest takie, że nie chcemy widzieć Seama w warstwach niższych niż warstwa prezentacji aby nie wiązać się z nim zbyt mocno mając na uwadze ogromną skalę czasową projektu. Spring pozwala na eleganckie zarządzanie transakcjami i bezpieczeństwem (Spring Security - dawniej ACEGI). Wogóle... Spring to Spring:)
Poza tym kwestia nieszczęsnych adnotacji. Wg mnie używanie adnotacji w celu Dependency Injection to chromy pomysł - zdradza on chyba niezrozumienie idei DI. Prosty kontrprzykład: w celu zmiany wstrzykiwanej instancji muszę przekompilować kod. O ile da się z tym żyć, to niestety gdybym chciał wstrzykiwać przykładowo różnie strategie wyliczania podatków u różnych klientów to przydały by się chyba jakieś dyrektywy kompilacji w Javie hehehe. Natomiast trzymanie deklaracji wstrzykiwania w np XMLu pozwala na trzymanie jednej wersji kodu - systemy różnych klientów różnią się jedynie paroma XMLami. Że o konfiguracjach na potrzeby testów nie wspomnę...
Do rzeczy: załóżmy, że mamy klasę juzkejsu i chcemy zadeklarować ją w Springu:
Teraz aby zrobić z naszego beana komponent Seamowy wystarczy do definicji beana dodać:
scope="prototype" - ponieważ Springowe singletony niezbyt nadają się na stanowe komponenty;)
<seam:component scope="CONVERSATION"/> - aby zadeklarować, że dany bean jest komponentem Seam o danym zasięgu.
Zatem całość wygląda tak:
Oczywiście aby parser XML mógł zrozumieć co to znaczy seam:component potrzebujemy zmodyfikować plik konfiguracyjny Springa:
Aby całość "zatrybiła", czyli aby nasze Springowe beany były widoczne w kontekście Seam i JSF musimy zadeklarować Resolvera w faces-config.xml:
Od tej pory w warstwie prezentacji (Czy to adnotacje @In w backing beanach czy wyrażenia EL w faceletach) możemy posługiwać się identyfikatorem naszego beana.
Oczywiście nasz przykład możemy rozszerzyć o managera transakcji i Proxy, który opakowuje nasze obiekty UC w celu obsłużenia transakcji w transparentny sposób:
Jednak tym razem musimy dodać:
class do seam:component oraz paramter proxyTargetClass ponieważ wspaniały Seam nie obsługuje Dynamic Proxy:/
Niestety nie da się wyciągnąć transaction proxy jako abstrakcyjny bean w celu zmniejszenia ilości kodu ponieważ mamy wówczas paskudny wyjątek:/
Prawie gotowe. Prawie ponieważ aby zaznaczyć początek i koniec Seamowej konwersacji muszę uzyć adnotacji @Begin i @End. Mogę je dopisać w metodach backing beanów, które reagują na czynności użytkownika. Niestety zakładam, że wiele różnych kliknięć może rozpocząć konwersację (np odpalić metodę init z klasy juzkesju) - więc mamy bałagan. Lepiej byłby otagować dwie metody juzkejsu - wszystko jasno, czysta i porządnie. Niestety nie mogę sobie pozwolić na brudzenie corowych warstw jakimiś adnotacjami jakiegoś frameworka prezentacji. Póki co wymyśliłem takie oto rozwiązanie:
W warstwie prezentacji tworzę klasę wrappera dla klasy juzkejsu: wrapper implementuje tan sam interfejs co UC, wrapper ma wstrzykniętą implementację danego UC (tą zadeklarowaną w Springu). Wrapper służy tylko do tego aby to jego metody (a nie klasy UC) brudzić adnotacjami. Hmm gdy wymyślę coś lepszego to napiszę...
//============================
Przedstawiona integracja jest wyjściem (aczkolwiek niewielkim) poza zakres "Poor man's integration" z rozdziału 15.2 doskonałej książki "Seam in Action" Dana Allena. Szczerze ją polecam wszystkim tym, którzy chcą zgłębić Seam trochę bardziej niż potrzeba do wyklikania formularzyków w apliakcji typu przeglądarka do bazy. Autor na prawdę rzetelnie opisuje poruszane zagadnienia. Szczególne wrażenie zrobił na mnie właśnie rozdział poświęcony integracji Springa z Seam. Oprócz gotowej do przepisania konfiguracji autor dokładnie omawia szczegóły architektoniczne obu frameworków i przyczyny dla których integracja wygląda tak a nie inaczej. Możecie się z niej dowiedzieć jak zintegrować frameworki na głębszym poziomie (wychodząc poza poor's man), tak aby wspólnie zarządzały persystencją i transakcjami. Mi niestety "nowoczesne" podejście do persystencji nie odpowiada z powodu drastycznych wymagań niefunckjonalnych...
Oczywiście można zadać pytanie: po co sie wydurniać? Czy używanie Springa to nie nakładanie gaci przez głowę? Przecież Seam wspiera injekcje, bijeckje, outjekcje...
Założenie jest takie, że nie chcemy widzieć Seama w warstwach niższych niż warstwa prezentacji aby nie wiązać się z nim zbyt mocno mając na uwadze ogromną skalę czasową projektu. Spring pozwala na eleganckie zarządzanie transakcjami i bezpieczeństwem (Spring Security - dawniej ACEGI). Wogóle... Spring to Spring:)
Poza tym kwestia nieszczęsnych adnotacji. Wg mnie używanie adnotacji w celu Dependency Injection to chromy pomysł - zdradza on chyba niezrozumienie idei DI. Prosty kontrprzykład: w celu zmiany wstrzykiwanej instancji muszę przekompilować kod. O ile da się z tym żyć, to niestety gdybym chciał wstrzykiwać przykładowo różnie strategie wyliczania podatków u różnych klientów to przydały by się chyba jakieś dyrektywy kompilacji w Javie hehehe. Natomiast trzymanie deklaracji wstrzykiwania w np XMLu pozwala na trzymanie jednej wersji kodu - systemy różnych klientów różnią się jedynie paroma XMLami. Że o konfiguracjach na potrzeby testów nie wspomnę...
Do rzeczy: załóżmy, że mamy klasę juzkejsu i chcemy zadeklarować ją w Springu:
<bean id="beanId" class="Klasa">
<property name="jakiesDAO"><ref bean="jakiesDAO"/></property>
</bean>
Teraz aby zrobić z naszego beana komponent Seamowy wystarczy do definicji beana dodać:
scope="prototype" - ponieważ Springowe singletony niezbyt nadają się na stanowe komponenty;)
<seam:component scope="CONVERSATION"/> - aby zadeklarować, że dany bean jest komponentem Seam o danym zasięgu.
Zatem całość wygląda tak:
<bean id="beanId" class="Klasa" scope="prototype">
<seam:component scope="CONVERSATION"/>
<property name="jakiesDAO"><ref bean="jakiesDAO"/></property>
</bean>
Oczywiście aby parser XML mógł zrozumieć co to znaczy seam:component potrzebujemy zmodyfikować plik konfiguracyjny Springa:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:seam="http://jboss.com/products/seam/spring-seam"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://jboss.com/products/seam/spring-seam
http://jboss.com/products/seam/spring-seam-2.0.xsd">
<!-- bean definitions -->
</bean>
Aby całość "zatrybiła", czyli aby nasze Springowe beany były widoczne w kontekście Seam i JSF musimy zadeklarować Resolvera w faces-config.xml:
<faces-config>
<application>
<el-resolver>
org.springframework.web.jsf.el.SpringBeanFacesELResolver
</el-resolver>
</application>
</faces-config>
Od tej pory w warstwie prezentacji (Czy to adnotacje @In w backing beanach czy wyrażenia EL w faceletach) możemy posługiwać się identyfikatorem naszego beana.
Oczywiście nasz przykład możemy rozszerzyć o managera transakcji i Proxy, który opakowuje nasze obiekty UC w celu obsłużenia transakcji w transparentny sposób:
<bean id="beanId" scope="prototype" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" >
<seam:component scope="CONVERSATION" class="Klasa"/>
<property name="proxyTargetClass" value="true"/>
<property name="transactionManager"><ref bean="txManager"/></property>
<property name="transactionAttributes">...</property>
<property name="target">
<bean id="beanId" class="Klasa" >
<property name="jakiesDAO"><ref bean="jakiesDAO"/></property>
</property>
</bean>
Jednak tym razem musimy dodać:
class do seam:component oraz paramter proxyTargetClass ponieważ wspaniały Seam nie obsługuje Dynamic Proxy:/
Niestety nie da się wyciągnąć transaction proxy jako abstrakcyjny bean w celu zmniejszenia ilości kodu ponieważ mamy wówczas paskudny wyjątek:/
Prawie gotowe. Prawie ponieważ aby zaznaczyć początek i koniec Seamowej konwersacji muszę uzyć adnotacji @Begin i @End. Mogę je dopisać w metodach backing beanów, które reagują na czynności użytkownika. Niestety zakładam, że wiele różnych kliknięć może rozpocząć konwersację (np odpalić metodę init z klasy juzkesju) - więc mamy bałagan. Lepiej byłby otagować dwie metody juzkejsu - wszystko jasno, czysta i porządnie. Niestety nie mogę sobie pozwolić na brudzenie corowych warstw jakimiś adnotacjami jakiegoś frameworka prezentacji. Póki co wymyśliłem takie oto rozwiązanie:
W warstwie prezentacji tworzę klasę wrappera dla klasy juzkejsu: wrapper implementuje tan sam interfejs co UC, wrapper ma wstrzykniętą implementację danego UC (tą zadeklarowaną w Springu). Wrapper służy tylko do tego aby to jego metody (a nie klasy UC) brudzić adnotacjami. Hmm gdy wymyślę coś lepszego to napiszę...
//============================
Przedstawiona integracja jest wyjściem (aczkolwiek niewielkim) poza zakres "Poor man's integration" z rozdziału 15.2 doskonałej książki "Seam in Action" Dana Allena. Szczerze ją polecam wszystkim tym, którzy chcą zgłębić Seam trochę bardziej niż potrzeba do wyklikania formularzyków w apliakcji typu przeglądarka do bazy. Autor na prawdę rzetelnie opisuje poruszane zagadnienia. Szczególne wrażenie zrobił na mnie właśnie rozdział poświęcony integracji Springa z Seam. Oprócz gotowej do przepisania konfiguracji autor dokładnie omawia szczegóły architektoniczne obu frameworków i przyczyny dla których integracja wygląda tak a nie inaczej. Możecie się z niej dowiedzieć jak zintegrować frameworki na głębszym poziomie (wychodząc poza poor's man), tak aby wspólnie zarządzały persystencją i transakcjami. Mi niestety "nowoczesne" podejście do persystencji nie odpowiada z powodu drastycznych wymagań niefunckjonalnych...
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
- Ś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
Subskrybuj:
Posty (Atom)