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

czwartek, 21 października 2010

Turbo Seam

Niniejszy post rozpoczyna mini serię poświęconą Seam Framework. Zawartość to kilka przydatnych technik oraz kilka trików, które udało mi się zebrać podczas paru lat używania tego "ficzer"-worka.

Spis treści

- Produktywne środowisko developerskie. Hot deploy wszystkiego. Nawet encji (a podobno się nie da;P) W najgorszym przypadku 3 sek. na przeładowanie.
- Integracja z Maven - elegancko i bezboleśnie. Panom od JBoss Tools już podziękujemy...
- Integracja z jQuery - gdy ciężar RichFaces przygniata, narzut na komunikację AJAX osłabia a kontrolki wyglądają jak z poprzedniej epoki
- Asynchroniczny mailing w dobrym stylu - mikro architektura rozsyłania maili połączona z eleganckim decoupliniem poprzez zdarzenia
- Integracja z BIRT - speszyl haki
- Seam Gen i Seam App. Framework - dlaczego nie używać:)


Powyższa lista będzie sukcesywnie aktualizowana o linki do konkretnych postów oraz ew. nowe pozycje. Docelowo post będzie stanowił swego rodzaju spis treści.

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

Generalnie do tej pory nie pisałem zbyt wielu postów technicznych - wychodząc z założenia, że w oceanie treści nie będą wnosić nic nowego.

Jednak zestawiając dwa aspekty:
- własne przemyślenia
- obserwując jak Seam jest używany
doszedłem do wniosku, że zgromadzona wiedza może się komuś przydać.

Jeżeli macie jakieś tematy, które chcielibyście poruszyć - piszcie śmiało, być może dodamy je do listy.

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

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:

<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