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ą Command Query Separation. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą Command Query Separation. Pokaż wszystkie posty
piątek, 15 kwietnia 2011
Pocztówka z wakacji
Mam za sobą 10-dniowe "wakacje", z których to wrażeniami chciałbym się podzielić.
Ostatnie 10 dni upłynęło mi na:
- konferencji 4 developers
- konferencji 33rd Degree
- szkoleniu na temat Distributed DDD i CqRS prowadzonemu przez Grega Younga
Podczas obu doskonałych konferencji obserwować można było wyraźnie pewne trendy.
Mimo, że konferencje mają charakter techniczny, to dużym powodzeniem cieszą się tematy miękkie. Pisał o tym już Martin Fowler w swoim artykule w akapicie na temat trendów konferencyjnych.
Generalne głosy ze świata są takie, że szczegóły techniczne można sobie zgłębiać w domu, a podczas konferencji mamy dostęp do wiedzy niedostępnej w literaturze z naszej branży. Cieszy fakt, że nadążamy za najnowszymi trendami - ciekawy jestem Waszych opinii na ten temat.
Kolejna obserwacja to wyraźny trend amerykanów - doskonała forma ale treść merytoryczna tak prosta, że miejscami ocierająca się o prostactwo (np. odpustowe sztuczki w aplikacjach klasy hello world;).
Podczas 33rd degree prowadziliśmy wraz z kolegami z ssepp panel dyskusyjny na temat profesji w naszej branży. Jako grupa zawodowa wciąż jednak zbytnio skupiamy się na rozgoryczeniu zamiast na działaniu w kierunku krystalizacji profesji.
Jednak trzydniowe szkolenie Grega było jeszcze bardziej intensywne pod względem mentalnym (towarzyskim również - wynajęliśmy na 5 dni wspólnie z Gregiem apartament:)
Greg przedstawił wiele szczegółów technicznych związanych z jego podejściem do architektury CqRS i modelowania DDD. Poszerzył nam również horyzonty przedstawiając architektury systemów, które obsługują setki tysięcy transakcji na sekundę!
Wniosek jest jeden: świat relacyjnych baz danych, popularnych frameworków i platform oraz standardowych warstw jest mocno ograniczający. Aby podejść do problemu o skali rzędu tysięcy na sekundę trzeba zaprzeczyć wszystko co stanowi naszą bazę wiedzy!
Klimat panujący na szkoleniu był nie do opisania. Ponad trzydzieści osób z kraju i zagranicy operujących wspólnym poziomie abstrakcji, fechtujących technikami inżynierii oprogramowania z mistrzowską gracją. Bez kompromisów jakościowych - po prostu przy tej klasie problemu nie da się inaczej...
Do tego doskonały prowadzący z szerokim wachlarzem kompetencji. Począwszy od szczegółów kompilatora poprzez paradygmaty programowania, wzorce projektowe i architektoniczne, znajomość całej klasyki oraz najnowszych zdobyczy inżynierii oprogramowania, metodyk prowadzenia projektów, kreowania wizerunku architektury jako produktu, technik negocjacji po ogólną erudycję sięgającą sieci neuronowych, algorytmów genetycznych, polityki itd itp.
Kudos Greg.
//==============================
Nazywam ten czas wakacjami, ponieważ jestem typem obserwatora (perceiving), poszukiwacza. Dlatego cenię sobie stałą stymulację nowymi pomysłami oraz dyskusje z ludźmi wnoszącymi coś do sprawy, ponad leżenie na plaży.
Patrząc z boku ciężko uwierzyć jak ktoś kto siedzi kilka godzin na konferencji/szkoleniu w bezruchu może przezywać ultra-intensywne doznania poznawcze:)))
środa, 30 marca 2011
Tydzień segregacji
Zbliżający się tydzień to dwie konferencje:
- 4 Developers
- 33rd Degree
Przygotowałem dla Was prezentację na temat Command-query Responsibility Segregation, którą przedstawię na obu konferencjach.
Poniżej wersja alfa.
Prezentacja zgodnie z najnowszymi trendami nie zawiera zbyt wiele tekstu - całość wymaga akompaniamentu paszczowego.
W świecie Javy mamy ostatnio stagnację (frameworki webowe już nie wychodzą co tydzień, a skoro najnowsze wersja Java EE wyszła rok temu to mamy kilka lat spokoju;). Jest to zatem dobry moment na ostrzenie piły - np zastanowienie się nad podstawami.
Ogólne przesłanie prezentacji: wybór narzędzia i podejścia powinien zależeć od klasy problemu.
W świecie Javy mamy z jednej strony platformę korporacyjną, która była pomyślana jako produkt dla klientów (wytwórców oprogramowania) wielkiej skali. Po nasyceniu tego rynku mamy widoczną zmianę strategii marketingowej i atakowanie małych softwarehousów. Z drugiej strony mamy mnogość rozwiązań typu generator jednowarstwowej aplikacji, którymi próbuje się kusić każdego - nawet duże softwarehousy.
W prezentacji przedstawię rozwiązania architektoniczne podpatrzone w świecie .NET. Nie są one specyficzne dla .NET ale w tej społeczności są mocniej rozwijane i dyskutowane niż w naszej.
Warto podkreślić, że koncepcja architektoniczna CqRS została wypracowana w ciągu kilku ostatnich lat przez community. Nie jest to zatem objawienie zesłane przez anonimowych pracowników jakiejś firmy, lecz wynik dyskusji i eksperymentów nad rzeczywistymi problemami. Jest to zatem owoc pracy ludzi żywo zaangażowanych w sprawę - stąd wartość.
Mottem dla mojej prezentacji będzie ewolucja architektury, czyli jej rozbudowa wraz ze wzrostem skali (komplikacji i wielkości) systemu. Wychodzę z założenia, że nie każde podejście może ewoluować.
Dla zainteresowanych prezentacją mam sugestię: warto wcześniej zapoznać się z moją prezentacją na temat Domain Driven Design. Nie jest to konieczne, ale jeżeli to zrobicie, to całość ułoży się w sumie w spójne rozwiązanie.
Link tutaj.
Tydzień segregacji właściwie zostanie wydłużony...
W kolejnym tygodniu Greg Young będzie prowadził otwarty wykład i 3-dniowe szkolenie na temat DDD i CqRS w Krakowie (szczegóły).
Nieczęsto zdarza się możliwość uczenia od mistrza, tak więc polecam:)
//=================================
Jeżeli ktoś ma uwagi/sugestie odnośnie prezentacji (treść, zakres) to piszcie w komentarzach lub na prv.
Będę bardzo wdzięczny za feedback:)
- 4 Developers
- 33rd Degree
Przygotowałem dla Was prezentację na temat Command-query Responsibility Segregation, którą przedstawię na obu konferencjach.
Poniżej wersja alfa.
Prezentacja zgodnie z najnowszymi trendami nie zawiera zbyt wiele tekstu - całość wymaga akompaniamentu paszczowego.
W świecie Javy mamy ostatnio stagnację (frameworki webowe już nie wychodzą co tydzień, a skoro najnowsze wersja Java EE wyszła rok temu to mamy kilka lat spokoju;). Jest to zatem dobry moment na ostrzenie piły - np zastanowienie się nad podstawami.
Ogólne przesłanie prezentacji: wybór narzędzia i podejścia powinien zależeć od klasy problemu.
W świecie Javy mamy z jednej strony platformę korporacyjną, która była pomyślana jako produkt dla klientów (wytwórców oprogramowania) wielkiej skali. Po nasyceniu tego rynku mamy widoczną zmianę strategii marketingowej i atakowanie małych softwarehousów. Z drugiej strony mamy mnogość rozwiązań typu generator jednowarstwowej aplikacji, którymi próbuje się kusić każdego - nawet duże softwarehousy.
W prezentacji przedstawię rozwiązania architektoniczne podpatrzone w świecie .NET. Nie są one specyficzne dla .NET ale w tej społeczności są mocniej rozwijane i dyskutowane niż w naszej.
Warto podkreślić, że koncepcja architektoniczna CqRS została wypracowana w ciągu kilku ostatnich lat przez community. Nie jest to zatem objawienie zesłane przez anonimowych pracowników jakiejś firmy, lecz wynik dyskusji i eksperymentów nad rzeczywistymi problemami. Jest to zatem owoc pracy ludzi żywo zaangażowanych w sprawę - stąd wartość.
Mottem dla mojej prezentacji będzie ewolucja architektury, czyli jej rozbudowa wraz ze wzrostem skali (komplikacji i wielkości) systemu. Wychodzę z założenia, że nie każde podejście może ewoluować.
Dla zainteresowanych prezentacją mam sugestię: warto wcześniej zapoznać się z moją prezentacją na temat Domain Driven Design. Nie jest to konieczne, ale jeżeli to zrobicie, to całość ułoży się w sumie w spójne rozwiązanie.
Link tutaj.
Tydzień segregacji właściwie zostanie wydłużony...
W kolejnym tygodniu Greg Young będzie prowadził otwarty wykład i 3-dniowe szkolenie na temat DDD i CqRS w Krakowie (szczegóły).
Nieczęsto zdarza się możliwość uczenia od mistrza, tak więc polecam:)
//=================================
Jeżeli ktoś ma uwagi/sugestie odnośnie prezentacji (treść, zakres) to piszcie w komentarzach lub na prv.
Będę bardzo wdzięczny za feedback:)
piątek, 25 marca 2011
Greg Young w Polsce! (UWAGA KONKURS)
Mamy już kompletną agendę konferencji 4Developers, zatem mogę oficjalnie powiedzieć, że Greg Young, którego udało się nam zaprosić do Polski, przedstawi dwie prezentacje:
- Zapomnij o szczegółach, liczy się efekt! - jest to nowa, jeszcze nie publikowana prezentacja, którą Greg przygotował specjalnie dla nas na 4Dev
- Uwolnij swoją domenę! - przekrojowa prezentacja, która udowadnia, że możemy skonstruować wysokowydajny, skalowalny system, zachowując nieautystyczny model domeny biznesowej. Czyli jak połączyć Domain Driven Design, Commmand-query Responsibility Segregation, Event Sourcing oraz kilka innych najnowszych zdobyczy inżynierii oprogramowania.
btw: czy ktoś z Was odważyłby się wystąpić ze slajdami w TAKIM stylu: 7 Reasons DDD Projects #FAIL
//=========================
KONKURS
Rejestracja na 4Dev będzie trwać już tylko do końca marca, a dla czytelników bloga mamy niespodziankę w postaci darmowej wejściówki.
Zasady są proste: wygrywa osoba o największej ilości komentarzy pod postami na moim blogasku. Chodzi oczywiście o posty wystawione przed ogłoszeniem konkursu:0 Zgłoszenia wraz z linkami do komentowanych postów przysyłajcie na maila podanego w sekcji "o mnie".
Proszę o udział tylko tych z Was, których potencjalne szanse na przybycie na konferencję wynoszą 99% (obecność sprawdzę osobiście;)
piątek, 22 października 2010
Warsjawa 2010
Zapraszam wszystkich czytelników bloga na prezentację Domain Driven Design - Wszystko ma swoje miejsce i wszystko jest na miejscu, którą przedstawiam w najbliższą sobotę na konferencji Warsjawa.
Skrót:
Czy zastanawialiście się co jest przyczyną rozkładu średnich i dużych systemów? Czy jest on nieunikniony i jest jedynie kwestią czasu? A może jednak istnieje jakiś sposób na utrzymanie entropii w ryzach?
Podczas prezentacji zobaczymy w jaki sposób Domain Driven Design pomaga w okiełznaniu chaosu. W prezentacji znajdą się główne techniki modelowania takie jak Ubiquitous Language, Bounded Context, Strategic Design.
Zostaną przedstawione również podstawowe techniki implementacji DDD: przykłady Building Blocks, Command-query Responsibility Segregation, system zdarzeń, przypadki wykorzystania ORM i SQL - całość na przykładach w Seam lub Spring.
//===========================
Dla tych z Was, którzy byli w Krakowie na NYAC zaznaczam, że core prezentacji będzie taki sam.
Skrót:
Czy zastanawialiście się co jest przyczyną rozkładu średnich i dużych systemów? Czy jest on nieunikniony i jest jedynie kwestią czasu? A może jednak istnieje jakiś sposób na utrzymanie entropii w ryzach?
Podczas prezentacji zobaczymy w jaki sposób Domain Driven Design pomaga w okiełznaniu chaosu. W prezentacji znajdą się główne techniki modelowania takie jak Ubiquitous Language, Bounded Context, Strategic Design.
Zostaną przedstawione również podstawowe techniki implementacji DDD: przykłady Building Blocks, Command-query Responsibility Segregation, system zdarzeń, przypadki wykorzystania ORM i SQL - całość na przykładach w Seam lub Spring.
//===========================
Dla tych z Was, którzy byli w Krakowie na NYAC zaznaczam, że core prezentacji będzie taki sam.
wtorek, 6 października 2009
Domain Specific Query
Zgodnie z obowiązującym trendem polegającym na prześciganiu się w publikowaniu bardziej lub jeszcze_bardziej oczywistych odkryć - dziś kolej na mnie i na coś na co wpadłem już jakiś czas temu.
Tytułowy wynalazek ma roboczą nazwę DSQ, którą triumfalnie nadałem mu kojarząc dwie modne techniki: Domain Specific Language i Command-query Separation.
NIEKRÓTKIE WYJAŚNIENIE
O CQS pisałem 2 miesiące temu w >tym< poście. W skrócie jest to styl architektoniczny, który zakłada separację API systemu na dwie wyraźne części. Jedna obsługuje (przyjmuje wysyłane do API) Command - rozkazy wykonania operacji. Druga natomiast obsługuje Query - obiekty specyfikujące kryteria pobierania/wyszukiwania danych.
W dzisiejszym poście skupimy się właśnie na tej drugiej części, czyli ogólnie mówiąc na wyszukiwarkach danych. Wyszukiwarki są dosyć proste - ot zwykłe servisy, pod którymi nie znajdują się już żadne kolejne warstwy - chyba, że potrzebujemy jeszcze "dodać coś od siebie" do pobranych z pewnego źródła danych.
DSL to koncepcja dużo bardziej popularna (w sensie ilości publikacji) niż CQS, więc nie będę się zbytnio rozpisywał na jej temat. Ogólnie rzecz ujmując chodzi o to aby kod logiki biznesowej czytać niemal jak język naturalny. Definiujemy sobie język zorientowany na konkretną domenę biznesową a później to już fraszka - program piszę się niemal sam;)
W statycznych językach typu Java nie mamy zbyt szerokiego pola do popisu jeżeli chodzi o zdefiniowanie własnego języka domenowego.
Jedyne co możemy zrobić w Javie to zastosować Fluent Interface i opisowe (samoopisujące wręcz) nazwy metod. Czyli jest to jedynie namiastka DSL, ale cieszy:)
Koncepcja Fluent Interface jest prosta: chodzi o to aby metody danej klasy zwracały instancję obiektu na rzecz którego zostały wykonane. Dzięki temu możemy zamiast pisać tradycyjnie:
możemy zmienić styl na bardziej fluent:
Jeżeli teraz zamiast nadawać metodom autystyczne nazwy (w stylu Bruce Eckela), wykażemy się odrobiną inwencji i słowiańskiej fantazji możemy pisać niemalże poetycko:
Chyba lepiej czyta się kod w takim stylu...
DO RZECZY
Po tym przydługim wstępie powinniśmy już mniej więcej czuć czym jest CQS i DSL...
Wyobraźmy sobie następujący problem :
W systemie przechowujemy dokumenty. Dokumenty mają szereg atrybutów: kilka typów statusów (związanych z przepływem dok. jak i technicznych), różnego rodzaju daty, przypisanie do różnych zasobów itp.
Domena biznesowa standardowa i może niezbyt wyszukana, ale dzięki temu wiadomo o co chodzi i możemy skupić się na samej technice.
Serwis, który miałby wyszukiwać dokumenty musiałby mieć metodę o kilkunastu parametrach. Pierwszym problem z taką metodą to wysoki ujemny współczynnik czytalności. Drugi problem to ciągłe zmiany sygnatury metody gdy pojawiają się nowe atrybuty dokumentu.
Zatem dosyć oczywiste jest, że stworzymy sobie klasę przechowującą atrybuty wyszukiwania a nasz serwis będzie miał metodę, do której przekazujemy paczkę kryteriów - po prostu jakiś rodzaj Query z CQS:
Jak do tej pory standard. (Query najpewniej będzie prędzej czy później serializowany.)
Teraz idąc dalej wg utartych szablonów dodalibyśmy do SearchDocumentsQuery kilkanaście pół prywatnych oraz oczywiście nasze ulubione gettery i settery.
Kod klasy klienckiej przy pomocy setterów ustawia kryteria wyszukiwania a kod metody DocumentsFinder.search przy pomocy getterów pobiera te parametry i klei sobie zapytanie w SQL, HQL tudzież mój ulubiony Hibernate Criteria API.
A gdzie miejsce dla DSL? No więc właśnie przy tak ordynarnym podejściu nie uświadczymy.
Załóżmy teraz, że chcemy hermetyzować kod klas klienckich (wywołujących DocumentsFinder)od szczegółów atrybutów Dokumentu.
Klient zamiast operować szczegółami:
Wyraża jedynie intencje co do chęci otrzymania dokumentów spełniających zestaw określonych specyfikacji biznesowych:
Gdzie przykładowo metody DSL wyglądają mniej więcej tak:
Jak interpretować powyższe metody ustawiające kryteria:
current - oznacza, ze życzymy sobie dokumentów będących aktualnie w obiegu.
Co to znaczy od strony modelu: że maja określony status i że wygasają np najwcześniej jutro.
contains - oznacza, że dokument ma zawierać określoną frazę. Z punktu widzenia modelu oznacza to, że tytuł lub treść dokumentu zawiera wszystkie słowa frazy.
Cała idea polega na tym, że klient nie wie nic o polach status, expiryDate, words, ...
Klient wyraża intencję.
Natomiast pola te są dostępne przez gettery dla DocumentsFinder, który to analizuje je i odpowiedzi składa zapytanie. Aby osiągnąć pożądaną widoczność getterów należałoby zdefiniować SearchDocumentsQuery jako klasę statyczną wewnętrzną w DocumentsFinder. Wówczas DocumentsFinder będzie widział metody prywatne, bo takie powinny być rozważane gettery. Statyczną ponieważ chcemy tworzyć Query bez "wywodzenia" jej z instancji Findera.
Kuszące może wydawać się dodanie do SearchDocumentsQuery metody buildEecutable(), która produkuje na podstawie stanu swoich wewnętrznych pól (statys, words,...) coś co można wykonać. Tym "czymś" może być PreparedStatement z JDBC, Criteria z Criteria API Hibenrate, Query z JPA, czy zwykły String zawierający SQL albo HQL.
Ale zastanówmy się: czy klasa, która jest widoczna przez klienta (np zdalnego) powinna mieć w sobie kod, który operuje na jakimś API np Hibernate (Criteria API) albo czy powinna wiedzieć coś o jakimś SQL? Przecież Finder może szukać w XMLu lub poprzez web service. Klient nie może tego wiedzieć - ba nie powinien.
//====================
Pomysł może mało odkrywczy i zapewne wiele osób wpadło na niego równolegle, ale jeszcze nie spotkałem nigdzie opisu.
Pomysł oczywiście nie będzie miał zastosowania w aplikacjach z małą abstrakcją pomiędzy bazą danych a GUI - tak zwanych przeglądarkach danych. W tego typu aplikacjach kontrolki na GUI (w tym pola filtrujące) odpowiadają zwykle niemal dosłownie kolumnom w bazie. W tego typu przypadkach próba abstrahowania od parametrów modelu jest niepotrzebnym uprawianiem dyscypliny pod tytułem nakładanie gaci przez głowę;)
Czekam na opinie, sugestie, krytykę - może być niekonstruktywna:)
Tytułowy wynalazek ma roboczą nazwę DSQ, którą triumfalnie nadałem mu kojarząc dwie modne techniki: Domain Specific Language i Command-query Separation.
NIEKRÓTKIE WYJAŚNIENIE
O CQS pisałem 2 miesiące temu w >tym< poście. W skrócie jest to styl architektoniczny, który zakłada separację API systemu na dwie wyraźne części. Jedna obsługuje (przyjmuje wysyłane do API) Command - rozkazy wykonania operacji. Druga natomiast obsługuje Query - obiekty specyfikujące kryteria pobierania/wyszukiwania danych.
W dzisiejszym poście skupimy się właśnie na tej drugiej części, czyli ogólnie mówiąc na wyszukiwarkach danych. Wyszukiwarki są dosyć proste - ot zwykłe servisy, pod którymi nie znajdują się już żadne kolejne warstwy - chyba, że potrzebujemy jeszcze "dodać coś od siebie" do pobranych z pewnego źródła danych.
DSL to koncepcja dużo bardziej popularna (w sensie ilości publikacji) niż CQS, więc nie będę się zbytnio rozpisywał na jej temat. Ogólnie rzecz ujmując chodzi o to aby kod logiki biznesowej czytać niemal jak język naturalny. Definiujemy sobie język zorientowany na konkretną domenę biznesową a później to już fraszka - program piszę się niemal sam;)
W statycznych językach typu Java nie mamy zbyt szerokiego pola do popisu jeżeli chodzi o zdefiniowanie własnego języka domenowego.
Jedyne co możemy zrobić w Javie to zastosować Fluent Interface i opisowe (samoopisujące wręcz) nazwy metod. Czyli jest to jedynie namiastka DSL, ale cieszy:)
Koncepcja Fluent Interface jest prosta: chodzi o to aby metody danej klasy zwracały instancję obiektu na rzecz którego zostały wykonane. Dzięki temu możemy zamiast pisać tradycyjnie:
Klasa obiekt = new Klasa();
obiekt.metoda1();
obiekt.metoda2();
obiekt.metoda3();
możemy zmienić styl na bardziej fluent:
Klasa obiekt = new Klasa();
obiekt.metoda1().metoda2().metoda3();
Jeżeli teraz zamiast nadawać metodom autystyczne nazwy (w stylu Bruce Eckela), wykażemy się odrobiną inwencji i słowiańskiej fantazji możemy pisać niemalże poetycko:
FabrykaWierszy.stworzUtwor().subtelny().zNutkaDekadencji().rymyNiechBedaCzestochowskie().strof(10);
Chyba lepiej czyta się kod w takim stylu...
DO RZECZY
Po tym przydługim wstępie powinniśmy już mniej więcej czuć czym jest CQS i DSL...
Wyobraźmy sobie następujący problem :
W systemie przechowujemy dokumenty. Dokumenty mają szereg atrybutów: kilka typów statusów (związanych z przepływem dok. jak i technicznych), różnego rodzaju daty, przypisanie do różnych zasobów itp.
Domena biznesowa standardowa i może niezbyt wyszukana, ale dzięki temu wiadomo o co chodzi i możemy skupić się na samej technice.
Serwis, który miałby wyszukiwać dokumenty musiałby mieć metodę o kilkunastu parametrach. Pierwszym problem z taką metodą to wysoki ujemny współczynnik czytalności. Drugi problem to ciągłe zmiany sygnatury metody gdy pojawiają się nowe atrybuty dokumentu.
Zatem dosyć oczywiste jest, że stworzymy sobie klasę przechowującą atrybuty wyszukiwania a nasz serwis będzie miał metodę, do której przekazujemy paczkę kryteriów - po prostu jakiś rodzaj Query z CQS:
public class SearchDocumentsQuery implements Serializable{
//atrybuty wyszukiwania + get/set
}
public class DocumentsFinder{
public Collection<Document> search(SearchDocumentsQuery criteria){
//..
}
Jak do tej pory standard. (Query najpewniej będzie prędzej czy później serializowany.)
Teraz idąc dalej wg utartych szablonów dodalibyśmy do SearchDocumentsQuery kilkanaście pół prywatnych oraz oczywiście nasze ulubione gettery i settery.
Kod klasy klienckiej przy pomocy setterów ustawia kryteria wyszukiwania a kod metody DocumentsFinder.search przy pomocy getterów pobiera te parametry i klei sobie zapytanie w SQL, HQL tudzież mój ulubiony Hibernate Criteria API.
A gdzie miejsce dla DSL? No więc właśnie przy tak ordynarnym podejściu nie uświadczymy.
Załóżmy teraz, że chcemy hermetyzować kod klas klienckich (wywołujących DocumentsFinder)od szczegółów atrybutów Dokumentu.
Klient zamiast operować szczegółami:
SearchDocumentsQuery criteria = new SearchDocumentsQuery();
criteria.setStatus(Status.ACTIVE);
criteria.setCreateDate(...)
criteria.set...
...
Collection<Document> result = documentsFinder.search(criteria);
Wyraża jedynie intencje co do chęci otrzymania dokumentów spełniających zestaw określonych specyfikacji biznesowych:
SearchDocumentsQuery criteria = new SearchDocumentsQuery();
criteria.current().contains("lorem ipsum");
Collection<Document> result = documentsFinder.search(criteria);
Gdzie przykładowo metody DSL wyglądają mniej więcej tak:
public class SearchDocumentsQuery implements Serializable{
private Status status;
private Date epiryDate;
private String[] titleWords;
private String[] contentWords;
public SearchDocumentsQuery current(){
status = Status.ACTIVE;
expiryDate = //data jutrzejsza
return this;
}
public SearchDocumentsQuery contains(String phrase){
String[] words = phrase.split(" ");//lub bardziej wyrafinowy split
titleWords = words;
contentWords = words;
return this;
}
}
Jak interpretować powyższe metody ustawiające kryteria:
current - oznacza, ze życzymy sobie dokumentów będących aktualnie w obiegu.
Co to znaczy od strony modelu: że maja określony status i że wygasają np najwcześniej jutro.
contains - oznacza, że dokument ma zawierać określoną frazę. Z punktu widzenia modelu oznacza to, że tytuł lub treść dokumentu zawiera wszystkie słowa frazy.
Cała idea polega na tym, że klient nie wie nic o polach status, expiryDate, words, ...
Klient wyraża intencję.
Natomiast pola te są dostępne przez gettery dla DocumentsFinder, który to analizuje je i odpowiedzi składa zapytanie. Aby osiągnąć pożądaną widoczność getterów należałoby zdefiniować SearchDocumentsQuery jako klasę statyczną wewnętrzną w DocumentsFinder. Wówczas DocumentsFinder będzie widział metody prywatne, bo takie powinny być rozważane gettery. Statyczną ponieważ chcemy tworzyć Query bez "wywodzenia" jej z instancji Findera.
public class DocumentsFinder{
public static class SearchDocumentsQuery implements Serializable{
private Status status;
private Date epiryDate;
private String[] titleWords;
private String[] contentWords;
//metoda contains i current - pominięto
private Status getStatus(){
return status;
}
}
public Collection<Document> search(SearchDocumentsQuery criteria){
if (criteria.getStatus() != null)//wywołanie prywatnej metody
//dołożenie do zapytania warunku na status
...
}
}
Kuszące może wydawać się dodanie do SearchDocumentsQuery metody buildEecutable(), która produkuje na podstawie stanu swoich wewnętrznych pól (statys, words,...) coś co można wykonać. Tym "czymś" może być PreparedStatement z JDBC, Criteria z Criteria API Hibenrate, Query z JPA, czy zwykły String zawierający SQL albo HQL.
Ale zastanówmy się: czy klasa, która jest widoczna przez klienta (np zdalnego) powinna mieć w sobie kod, który operuje na jakimś API np Hibernate (Criteria API) albo czy powinna wiedzieć coś o jakimś SQL? Przecież Finder może szukać w XMLu lub poprzez web service. Klient nie może tego wiedzieć - ba nie powinien.
//====================
Pomysł może mało odkrywczy i zapewne wiele osób wpadło na niego równolegle, ale jeszcze nie spotkałem nigdzie opisu.
Pomysł oczywiście nie będzie miał zastosowania w aplikacjach z małą abstrakcją pomiędzy bazą danych a GUI - tak zwanych przeglądarkach danych. W tego typu aplikacjach kontrolki na GUI (w tym pola filtrujące) odpowiadają zwykle niemal dosłownie kolumnom w bazie. W tego typu przypadkach próba abstrahowania od parametrów modelu jest niepotrzebnym uprawianiem dyscypliny pod tytułem nakładanie gaci przez głowę;)
Czekam na opinie, sugestie, krytykę - może być niekonstruktywna:)
czwartek, 3 września 2009
Lazy loading w kontekście paradygmatu Command-query Separation

ODŚWIEŻENIE KONTEKSTU
Kilka miesięcy temu popełniłem dwa posty na temat Lazy loadingu (wiem, że nie chce się Wam ich czytać więc streszczam):
Lazy Loading a sprawa wydajności - post traktujący ogólnie o drastycznym spadku wydajności w sytuacjach gdy LL jest stosowany w niewłaściwym momencie.
Chodzi głównie o klasyczny "n+1 Select problem", który potrafi totalnie zamulić system. W skrócie: problem pojawia się gdy pobieramy z EnityManagera/Sesji hibernate kolekcję a następnie iterujemy po niej i getterami pobieramy zagregowane obiekty. Wówczas do bazy wysyłane jest 1 zapytanie o kolekcję oraz dla każdego z jej n elementów koleje zapytania dociągające potrzebne obiekty.
Podobne schorzenie występuje gdy naiwnie ustawimy w mapowaniu FetchMode na EAGER.
DAO a sprawa Lazy Loading - post poruszający problem tak zwanej cieknącej abstrakcji, czyli "brudzenia" kodu logiki warstw wyższych szczegółami technicznymi typu zamknięta sesji persystencji.
Oczywiście mamy sprawdzony sposób na cieknącą abstrakcję - podejście Open Session in View (rozwiązanie w Springu np przez Mateusza Mirackiego). Niestety w tym przypadku łatwo dopuścić do opisanego powyżej "n+1 Select Problem". Ciekawe rozwiązanie z testowaniem ilości wysyłanych do bazy zapytań przy pomocy statystyk Hibernate zaproponował w komentarzu do mojego posta Milus.
CREDO
Od razu śpieszę wyjaśnić - żeby nie było, że jestem jakimś fanatycznym wrogiem Lazy loadingu czy ORM w ogólności. Wręcz przeciwnie, namiętnie go stosuję od 5 lat i uważam za bardzo wygodny młotek w większości sytuacji.
PROBLEM
Od czasu opublikowania tych postów dostałem kilka maili z zapytaniami o sposoby radzenia sobie z powyższymi problemami.
Pod postami wywiązały się też krótkie dyskusje prezentujące możliwe podejścia.
Właściwie to chodzi głównie o problemem z wydajnością, ponieważ filozoficzny problem cieknącej abstrakcji prawie nikogo nie boli. Nauczyliśmy się z nim żyć i raczej się nad nim nie zastanawiamy (na szczęście Jacek podziela moje rozterki).
PRAWDZIWY PROBLEM
Wg mnie prawdziwy problem leży w samym podejściu do dostępu do danych, czyli architekturze aplikacji. Problem polega na zbytnim uogólnieniu.
Tak samo traktujemy dwa diametralnie różnie rodzaje obiektów:
- obiekty domenowe, które wykonują operacje biznesowe (lub na których to wykonujemy modyfikacje w podejściu proceduralnym)
- dane "przekrojowe" potrzebne jedynie do prezentacji (np wyświetlenia na GUI) wycinka aktualnego stanu systemu
CQS
Teraz nadszedł wreszcie czas na przedstawienie tytułowego bohatera tego posta: zapomniany i zakurzony paradygmat: Command-query Separation.
Paradygmat tez zakłada, że system posiada "interfejs", przez który wysyłamy do niego polecenia oraz osobny, przez który odpytujemy o dane. Nigdy nie projektujemy operacji, które zarówno coś modyfikują jak i odczytują dane.
W jaki sposób możemy wykorzystać to podejście w systemach enterprise?
"Interfejsem" przez który klienty (nie klienci) komunikują się z systemem może być warstwa aplikacji. Cienka warstwa, która zajmuje się wszystkim, oprócz logiki biznesowej i dostępu do danych.

COMMAND
Ok, chcemy coś zrobić w systemie, wysyłamy do niego Polecenie. Tak jak wspomniałem "interfejsem" jest warstwa aplikacji "opublikowana" jako jakieś bezstanowe servisy lub obiekty stanowe - zależnie od wymagań.
Jeżeli klient wyśle do tej warstwy Command, wówczas pobiera ona z Repozytorium jakieś encje (lub agregaty w DDD). Dalej na encjach/agregatach uruchamiamy ich metody biznesowe - jeżeli bawimy się obiektowo, lub wywołujemy jakieś biznesowe servisy przekazując im jako parametry pobrane właśnie encje. Nic specjalnego, klasyczna architektura warstwowa.
Natomiast w tym właśnie przypadku - gdy wysyłamy do systemu Command mający zwykle na celu wykonanie jakiś operacji biznesowych - jak najbardziej możemy (ba powinniśmy) radośnie korzystać z Lazy loadingu. Jest to jak najbardziej właściwy moment ponieważ natura takich operacji jest zwykle taka, że pobieramy kilka obiektów, które wchodzą ze sobą w jakąś interakcję (lub władają nimi servisy). Rzeczone obiekty biznesowe ewentualnie potrzebują do wypełnienia swej biznesowej odpowiedzialności zagregowanych składników. Raczej nic złego się nie stanie, gdy zamiast 3 prostych zapytań do bazy wyślemy ich 5 czy nawet 10.
Czasem nawet będzie to bardziej wskazane niż join - zależy do natury danych.
Cała operacja wykonuje się w obrębie metody z warstwy aplikacji więc jest objęta transakcją i ma cały czas otwartą sesję persystencji więc nie martwimy się o wyjątki Lazy loadingu.
QUERY
Natomiast jeżeli do systemu trafia Query, czyli zapytanie o dane, to wówczas sprawa wygląda nieco inaczej...
Przede wszystkim warstwa aplikacji nie ma pod sobą warstwy z logiką (żadnych servisów biznesowych). Czy potrzebujecie abstrakcji dostępu do danych (DAO/Repozytorium)? Raczej nie zmienicie nigdy źródła danych. Nie ma sensu również testowanie jednostkowe "Finderów" aplikacyjnych z podmienionymi na mocki DAO. Czyli czyste pobieranie danych.
W mniej złożonych systemach możemy sobie pozwolić na zwrócenie encji w odpowiedzi na kwerendę.
W bardziej poważnych raczej nie możemy pozwolić sobie na ujawnianie klientom naszego modelu, więc zwrócimy jakiś Data Transfer Object (DTO). Hermetyzacja modelu to podstawa - dzięki temu może on ewoluować iteracyjnie bez obaw o zniweczenie pracy teamu dospawującego prezentację.
QUERY ZWRACAJĄCE ENCJE
Jeżeli zdecydujemy się na zwracanie encji to musimy uporać się z paroma problemami technicznymi:
- wydajność: zwykle scenariusz obsługi kwerendy to pobranie kolekcji danych. Wówczas mamy jak w banku opisany na wstępie "n+1 Select problem". Rozwiązanie jest bardzo proste - wystarczy się pofatygować i napisać zapytanie z klauzulą JOIN FETCH. Przykładowo SELECT p FROM Person p JOIN FETCH p.addresses - dzięki temu chciwie/łapczywie (nie wiem, na które rozkoszne tłumaczenie się zdecydować) pobierzemy osoby wraz z podciągniętymi adresami. Po prostu ORM wygeneruje SQLa z JOINem.
Niezbyt dobrym pomysłem jest ustawienie w mapowaniu powiązania obiektów z FetchMode.EAGER. Spowoduje ono, że zawsze wyciągając jeden obiekt pobierzemy jego "dziecko". Owszem są sytuacje, gdzie z kontekstu biznesowego takie podejście jest sensowne, ale zwykle stanowią zdecydowaną mniejszość. Zwykle w jednym Use Case zależy nam na pobraniu np osób z adresami a winnym adresy są zbędne.
Warto pamiętać, że domyślnie strategia EAGER obowiązuje dla powiązań wiele-jeden, jeden-jeden i warto ją wyłączać.
- Open Session in View - podejście to o ile jest wygodne to niestety pozwala łatwo zapomnieć o tym, że leniwie podciągamy jakieś dane. Po prostu istnieje niemała szansa, że na widoku odwołamy się do adresów osoby a w zapytaniu zapomnimy dopisać JOIN FETCH. Działa? Działa. Muli? W środowisku developerskim z małą ilością danych pewnie nie;P
- Ilość danych - zwykle Use Case gdzie do systemu trafia Query zakłada, że z bazy trzeba pobrać dane "przekrojowe". Czyli dane z wielu tabel, ale z każdej z nich interesuje nas zaledwie kilka kolumn. W małych systemach, gdzie warstwa GUI i warstwa aplikacji stoją na tej samej JVM będzie to w śmigać.
Ale nawet w takiej konfiguracji mamy problem z pobieraniem zbędnych danych. Przykładowo: gdy pobieramy z bazy np dokumenty aby jedynie wyświetlić ich listę (data, autor) a każdy z nich ma kolumnę przechowującą dziesiątki stron textu. Rozwiązaniem jest leniwe ładowanie pól - czyli ich nieładowanie:) W hibernate wymaga to poddanie skompilowanego bytecodu instrumentalizacji.
Innym podejściem może być zamapowanie tabeli przez kilka klas. Przykładowo DocumentFull, DocumentLight, itp... Jeżeli czujecie niesmak na myśl o mnożeniu bytów to nie jesteście sami.
Hibernate pozwala na pobieranie danych wprost do DTO. Tworzymy DTO szyte na miarę danego Use Case, a składnia wygląda tak:
SELECT new pakiet.KlasaDTO(pole1, pole2.podpole) FROM...
Oczywiście przy założeniu, że odpowiedni konstruktor istnieje.
QUERY ZWRACAJĄCE DTO
Jeżeli zdecydujemy się na zwracanie DTO to zapewne dlatego, że potrzebne dane są na tyle przekrojowe, że żaden zestaw encji nie modeluje ich sensownie (i optymalnie).
Innym powodem może być chęć hermetyzacji zmiennego modelu poza stabilną anticorruption layer. Warstwa zapobiegająca gniciu to pojęcie z DDD i ma pragmatyczny sens w nieco bardziej perspektywicznych projektach.
Poza tym możemy być dumni, że nawet nasza architektura wspiera Agile umożliwiając ewolucję modelu domenowego bez rujnowania wszystkiego dookoła:)
Częstym błędem w tym przypadku jest pobieranie z ORM encji a następnie przepakowywanie ich w DTO.
Nie tędy droga...
Pobierzmy tylko to co tak na prawdę jest potrzebne. Najprościej zrealizować to z użyciem wspomnianej konstrukcji Hibernate SELECT NEW. Jednak w złożonych systemach zwykle nieodzowny będzie co najmniej w paru jakiś szyty na miarę i zoptymalizowany SQL. Jakąś abstrakcją nad SQLem może być wówczas np iBATIS mapujący result na DTO.
Jak to zwykle bywa najlepsze będzie podejście hybrydowe. Tam gdzie możemy na to sobie pozwolić zwracamy w wyniku obsługi Query encje - zwiększając tym samym swą produktywność. Natomiast tam gdzie krytyczna jest wydajność lub specjalna struktura danych, zwracamy DTO.
//=======================================
Opisane powyżej aspekty wydajności związane z Lazy Loadingiem aplikują się do systemów każdej wielkości - szczególnie "n+1 Select problem". Natomiast rozwiązanie z CQS a szczególnie podejście gdzie zwracamy z kwerendy DTO jest bardziej pracochłonne, przez co aplikuje się do projektów bardziej perspektywicznych.
Ale czy jest sens tworzyć w JEE projekty inne niż perspektywiczne? Do prostych i szybkich zadań typu "przeglądarka do bazy" jest przecież Microsoft Access;P
poniedziałek, 9 marca 2009
Undergroundowy Domain Driven Design
"Kraków - dawna stolica Polaków"
W sobotę odbyła się w Krakowie konferencja 4develoeprs, na której to byłem miałem (że pozwolę sobie napisać w staropolskiej składni zaprzeszłej będąc natchnionym pobytem w mieście poetów i masakrycznych dziur w asfalcie) przyjemność przedstawić krótką prezentację.
Tym, którzy zastanawiają się jakim cudem pojawiłem się na salonach obok takich znamienitości jak Adam Bien czy Neal Ford śpieszę wyjaśnić iż chodzi o Java Underground.
JU, którego organizatorem był Grzegorz Duda polegał (w skrócie) na przedstawieniu prezentacji w ciągu 5 minut, a brali w nim udział: Adam Bien (EJB), Bartek Kuczyński (JavaFX), Szymon Jachim (Scala), Bartek Chrabski (Jazz) no i ja (o Jacku L. który nie dotarł nie wspomnę;)
Aby zmieścić się w konwencji jako temat wybrałem sobie pewne "undergroundowe" aspekty architektury pojawiające się w DDD. Jako, że temat spotkał się z pewnym zainteresowaniem, a ja w ciągu pięciu minut byłem w stanie wydobyć z siebie jedynie turbo-bełkot, rozwinę w tym poście nieco bardziej każdy ze slajdów.

O ile samo podejście DDD nie jet już undergroundem to implikacje wynikające z podążania za nim można już do undergroundu zaliczyć. Ortodoksyjne przestrzeganie zasad DDD prowadzi do pojawienia się na poziomie projektu i implementacji pewnych konstruktów, które mogą się wydawać obrazoburcze, szczególnie w porównaniu z tradycyjnym podejściem, lansowanym przez różnego rodzaju frameworki w różnego rodzaju tutorialach.

No więc właśnie... silnie zakorzeniona tradycja każe umieszczać algorytmy w procedurach a dane w strukturach danych.

Źródła tej tradycji sięgają co najmniej szalonych lat '70 (pewnie i wcześniej), kto programował w turbo pascalu ten zapewne widzi analogię pomiędzy servisami a procedurami oraz encjami a rekordami.
Tradycja proceduralna jest mocno ugruntowana i nie można jej kwestionować. Doskonale nadaje się do modelowania tych domen, które z natury są proceduralne. Niestety nie sprawdza się zbyt dobrze w przypadku problemów z domen o naturze obiektowej.
Warto zastanowić się dobrze w początkowej fazie projektu nad naturą domeny. Nie warto walczyć z naturą - nikt jeszcze tej walki nie wygrał. Wszyscy wiemy czym kończy się proceduralne podejście stosowane do skomplikowanych (nie znaczy dużych!) domen... Prędzej czy później pojawia się ON. Wyłania się z plątaniny zależności i powielonej logiki... Przeklęty demon... Potwór Spaghetti.

Właściwie to w klasycznym podejściu będzie to Potwór Lasagne (Spaghetti z warstwami;)

Koncepcja DDD pojawiła się jako odpowiedź na problemy z komplikacją logiki biznesowej. DDD zakłada, że warstwa logiki domenowej jest najważniejszym elementem systemu i jednocześnie najtrudniejszym do opracowania. Jest to serce systemu, ale serce bardzo kruche. Niepoprawny model nie jest odporny na zmiany (której jak wiemy z doświadczenia są pewne jak śmierć i podatki - podatki to chyba nie u nas).
DDD nie jest żadną technologią, platformą ani frameworkiem. Jest to jedynie zbiór zasad, wytycznych, wzorców i dobrych praktyk, których stosowanie ma zapewnić zaprojektowanie porządnego modelu domeny.
DDD to dosyć obszerna "filozofia", natomiast na potrzeby przykładów undergroundowych architektur zobaczymy tylko mały jej fragment. Przy okazji warto przypomnieć, że DDD aplikuje się raczej do złożonych problemów. Pytanie tylko, kto potrafi przewidzieć gdzie zabrnie projekt - już parę razy spotkałem się z projektami, które na początku wydawał się "szybką akcją bydgoskiej milicji". Nota bene: proste problemy powinno rozwiązywać się chyba w Excelu; ostatecznie w Accesie;) Ale jeżeli już czujemy silną wewnętrzną potrzebę aby wydurniać się z programowaniem prostych problemów i nie chcemy robić tego proceduralnie to zawsze można skorzystać z podejścia DDD Lite.

DDD tak na prawdę nie jest niczym nowym. To po prostu zestaw dobrych praktyk opartych na starych dobrych (ale nie tak starych i nie tak dobrych jak Turbo Pascal;) koncepcjach obiektowej analizy i projektowania. Wystarczy po prostu ich nie negować i nie programować wbrew nim - a jest to konieczne aby osiągnąć pewien cel...
DDD zakłada unifikację modelu analitycznego i projektowego, o którym już pisałem: UP-DDD in Action: Malutka Teoria Unifikacji. Cały zespół, począwszy od analityków, poprzez projektantów aż po koderów powinien posługiwać się wspólnym językiem opisującym domenę - zwanym w DDD Ubiquitous Language. Niestety aby było możliwe dosłowne zaprojektowanie pomysłów analityka musimy posługiwać się pełnym wachlarzem środków wyrazu języków OO. Procedury i encje nie wystarczą - o tym dalej przy okazji archetypów.

DDD zakłada modelowanie warstwy logiki domenowej przy użyciu pewnych standardowych klocków, zwanych Building Blocks. Jest to jedna z głównych koncepcji. Na pierwszy rzut oka może się wydawać, że jest ich całkiem dużo, ale każdy ma uzasadnienie swego istnienia wynikające z koncepcji Responsibility Driven Design, o której pisałem.

Artefakty już opisywałem, więc jedynie gwoli przypomnienia:
W ortodoksyjnym podejściu encje nie posiadają nawet getterów i setterów. To jest totalny underground w porównaniu z klasyką. Niewielu programistów potrafi to zaakceptować i dla tego chowają się za murem dysonansu kognitywnego.
Pewnie zastawiasz się: jeżeli nie ma nawet getterów/setterów to w jaki sposób wyświetlić encję na formularzu? Albo jak ją zmienić? Odpowiedź już niedaleko...

Nieograniczanie się do anemicznych (pozbawionych odpowiedzialności) encji oraz prymitywnych servisów ma kolejną niebagatelną zaletę. Posługiwanie się bogatym zestawem Building Blocks oraz korzystanie z bogactwa technik OO pozwala niemal dosłownie implementować zaawansowane i złożone modele analityczne. Na przykład modele wyprowadzone z archetypów. O archetypach już w niedalekiej przyszłości pojawię się parę postów na tym blogu, ponieważ IMHO jest to zdecydowanie ważniejsze niż kolejny gówniany framework webowy.
Póki co przyjrzyjmy się przykładowemu modelowi użytkowników na powyższym slajdzie... Piękny, prawda? Niestety nadaje się jedynie na przykłady w książkach lub jako model w projektach "Hello World".

Chcąc nieco bardziej profesjonalnie zaprojektować model np. użytkowników możemy
a) pomyśleć - co nie gwarantuje sukcesu
b) skorzystać z archetypu o nazwie "party" ("uczestnik", nie "impreza")
Tak jak wspomniałem o archetypach będzie później, ale zwróćmy uwagę na (pozorną) komplikację powyższego modelu. Jest fajny, ponieważ można w nim przedstawić dowolną relację pomiędzy dowolnymi uczestnikami (np ja mogę być klientem jakiejś firmy jak i jej pracownikiem; firma ta może być klientem mojej żony; itp).
Ale życzę powodzenia implementując ten (czy inny) zaawansowany archetyp przy pomocy klasycznego podejścia - anemicznego modelu. Widzicie już oczyma wyobraźni te łańcuszki 15 kropek i getterów - train wreck.
Z wykorzystaniem technik DDD możemy w sposób naturalny (OO) i bezproblemowy zaimplementować ten model a całą komplikację ukryć w hermetycznym agregacie (grafie encji), który operuje na swej wewnętrznej skomplikowanej strukturze poprzez metody biznesowe.

Undergroundowe w DDD jest również podejście do zależności. Otóż zależności na poziomie tej samej warstwy nie są niczym złym. Przecież zależności odzwierciedlają model biznesu, więc nie ma potrzeby aby się wydurniać z wstrzykiwaniem zależności czy tworzeniem na siłę interfejsu dla każdego komponentu biznesowego. Wiem, że niektóre platformy od niedawna;) obsługują Wstrzykiwanie Zależności i wymagają wszędzie interfejsów, ale do wszystkiego trzeba podejść z rozsądkiem.
Natomiast zależności modelu domenowego od klas infrastruktury są niepożądane. Nawet gdy klasy te są przykryte interfejsem - to nic nie zmienia, może co najwyżej poprawia nam samopoczucie.
Jedyna techniczną zależność modelu biznesowego jaką dopuszcza DDD jest zależność od szyny komunikatów. Obiekt biznesowy w momencie, gdy poczuje taką potrzebę może co najwyżej stworzyć komunikat i puścić go do pipe (która zawiera zwykle filtry). Jest to architektura pipeline.

Klasyczne podejście warstwowe również nie sprawdza się zbyt dobrze w przypadku DDD. Na powyższym slajdzie widzimy 2 możliwości:
Możemy "podpinać" obiekty biznesowe (te, które mają jakiś stan) pod GUI. Niestety wiąże się to z pewną niezręcznością: GUI "widzi" metody biznesowe. Poza tym obiekty biznesowe muszą oferować gettery i settery w celu dokonywania modyfikacji na nich. Niestety narusza to obiektowy paradygmat enkapsulacji, czyli prowadzi do pornografii obiektowej - odkrywania wewnętrznych struktur.
Podejście to ma jeszcze jedną wadę. Implementowanie setterów pozwala na istnienie obiektu w stanie niespójnym. Przykładowo zmieniając setterami encję Adres możemy zmienić miasto ale nie zmieniać kodu pocztowego:P
Mimo tych wad, podejście tego typu jest często stosowane w "prostych" przypadkach.
Strzałka po prawej stronie diagramy prezentuje inne podejście. W warstwie aplikacji obiekty biznesowe są przepakowywane do/z obiektów transportowych DTO. Wiem, że przepakowywanie obiektów kojarzy się z robotą szympansa, ale dzięki temu możemy uniknąć powyższych wad i niezręczności. Zaraz zaraz... jak pobrać stan obiektu biznesowego gdy nie ma on getterów? Myśl obiektowo; nie pytaj, wydawaj rozkazy! Oto rozwiązanie.
Podejście opierające się na przepakowywaniu ma jednak wadę polegającą na tym, że zupełnie niepotrzebnie pobieramy z repozytorium nadmierną ilość danych - ponieważ dany obiekt transferowy (potrzebny w danym przypadku użycia np. dla danego ekranu) może potrzebować znacznie mniej danych niż zawiera np agregat.

O ile powyższe podejścia do warstw są zadowalające, to prawdziwy underground pojawia się gdy uznamy, że jeden wymiary (wysokość warstw) nie wystarcza i potrzebujemy drugiego - heh to stary trik fizyków. Gdy brakuje im wymiarów aby upchać swe teorie to zakładają istnienie nowych -stąd w różnych odmianach Teorii Strun 11 a nawet 26 wymiarów czasu i przestrzeni;P
Ortodoksyjna architektura DDD wymaga istnienia 2 stosów. Jeden do "prymitywnych" operacji CRUD, drugi natomiast do obsługi rozkazów dla modelu domenowego. Podejście to zapewnia nam utrzymanie eleganckiego stylu Command-Query Separation. Stylu w którym polecenia odczytu stanu i polecenia zmiany stanu są jasno i przejrzyście odseparowane. Posiadanie osobnych klas manipulacyjnych stanem pozwala łatwo audytować i kontrolować ten kod.
Zauważmy, że stos DDD jest tylko do zapisu. Dokonujemy w nim zmian wysyłając komunikaty z poleceniami (wzorzec command). Obiekty biznesowe mogą ew. wygenerować swoje komunikaty i umieścić je w pipeline. Natomiast klient może odpytać o stan jedynie poprzez stos CRUD wysyłając komunikat z kwerendą.
Dodatkowo stos CRUD może zwracać Encje (nie biznesowe a anemiczne - czyli "normalne") będące obiektami transferowymi "szytymi" na miarę.
Separacja przetwarzania logiki od magazynu danych pozwala tworzyć architektury przygotowane na masowe obciążenie i skalowanie - DDDD (4xD to nie pomyłka, ale Distributed DDD). Architektury, gdzie liczy się coś więcej niż procedury i encje. Architektury, w których 2 stosy odpowiadają za osobne moduły z osobnymi magazynami danych w szczególności. Magazynami, które mogą być optymalizowane względem czasu wykonania (stos Command) i czasu wykonywania przekrojowych raportów (stos Query). Magazyny, które mogą asynchronicznie się komunikować i synchronizować.
Ale to temat na osobnego posta... Zainteresowanym polecam wywiad z Gregiem Youngiem - jednym z guru DDD.
//=========================
Tak wyglądają lekkie modyfikacje standardowych architektur warstwowych dopasowane do DDD i DDDD. W świecie Javy jest to jeszcze underground...
Ostatnią z prezentacji, która zakończyła moją sesję na 4developers była "Understanding Distributed Messaging Systems and Where They Fit" Raymonda Lewallena
ze ścieżki .NET.
Raymond opowiadał o podstawowych architekturach dużych i skalowalnych systemów rozproszonych opartych o komunikaty. Jakież było moje zdziwienie gdy Raymond wspomniał nagle coś o Agregacie, później o tym, że wysyła on komunikat do pipleina aż wreszcie zobaczyłem diagram architektury 2-stosowej. Dla niego było to takie oczywiste i naturalne, że używa DDD... nawet o tym nie wspomniał i nie robił żadnego wprowadzenia... po prostu standard:)
Chyba chłopaki od .NET wiedzą w co się bawić;P
W sobotę odbyła się w Krakowie konferencja 4develoeprs, na której to byłem miałem (że pozwolę sobie napisać w staropolskiej składni zaprzeszłej będąc natchnionym pobytem w mieście poetów i masakrycznych dziur w asfalcie) przyjemność przedstawić krótką prezentację.
Tym, którzy zastanawiają się jakim cudem pojawiłem się na salonach obok takich znamienitości jak Adam Bien czy Neal Ford śpieszę wyjaśnić iż chodzi o Java Underground.
JU, którego organizatorem był Grzegorz Duda polegał (w skrócie) na przedstawieniu prezentacji w ciągu 5 minut, a brali w nim udział: Adam Bien (EJB), Bartek Kuczyński (JavaFX), Szymon Jachim (Scala), Bartek Chrabski (Jazz) no i ja (o Jacku L. który nie dotarł nie wspomnę;)
Aby zmieścić się w konwencji jako temat wybrałem sobie pewne "undergroundowe" aspekty architektury pojawiające się w DDD. Jako, że temat spotkał się z pewnym zainteresowaniem, a ja w ciągu pięciu minut byłem w stanie wydobyć z siebie jedynie turbo-bełkot, rozwinę w tym poście nieco bardziej każdy ze slajdów.

O ile samo podejście DDD nie jet już undergroundem to implikacje wynikające z podążania za nim można już do undergroundu zaliczyć. Ortodoksyjne przestrzeganie zasad DDD prowadzi do pojawienia się na poziomie projektu i implementacji pewnych konstruktów, które mogą się wydawać obrazoburcze, szczególnie w porównaniu z tradycyjnym podejściem, lansowanym przez różnego rodzaju frameworki w różnego rodzaju tutorialach.

No więc właśnie... silnie zakorzeniona tradycja każe umieszczać algorytmy w procedurach a dane w strukturach danych.

Źródła tej tradycji sięgają co najmniej szalonych lat '70 (pewnie i wcześniej), kto programował w turbo pascalu ten zapewne widzi analogię pomiędzy servisami a procedurami oraz encjami a rekordami.
Tradycja proceduralna jest mocno ugruntowana i nie można jej kwestionować. Doskonale nadaje się do modelowania tych domen, które z natury są proceduralne. Niestety nie sprawdza się zbyt dobrze w przypadku problemów z domen o naturze obiektowej.
Warto zastanowić się dobrze w początkowej fazie projektu nad naturą domeny. Nie warto walczyć z naturą - nikt jeszcze tej walki nie wygrał. Wszyscy wiemy czym kończy się proceduralne podejście stosowane do skomplikowanych (nie znaczy dużych!) domen... Prędzej czy później pojawia się ON. Wyłania się z plątaniny zależności i powielonej logiki... Przeklęty demon... Potwór Spaghetti.

Właściwie to w klasycznym podejściu będzie to Potwór Lasagne (Spaghetti z warstwami;)

Koncepcja DDD pojawiła się jako odpowiedź na problemy z komplikacją logiki biznesowej. DDD zakłada, że warstwa logiki domenowej jest najważniejszym elementem systemu i jednocześnie najtrudniejszym do opracowania. Jest to serce systemu, ale serce bardzo kruche. Niepoprawny model nie jest odporny na zmiany (której jak wiemy z doświadczenia są pewne jak śmierć i podatki - podatki to chyba nie u nas).
DDD nie jest żadną technologią, platformą ani frameworkiem. Jest to jedynie zbiór zasad, wytycznych, wzorców i dobrych praktyk, których stosowanie ma zapewnić zaprojektowanie porządnego modelu domeny.
DDD to dosyć obszerna "filozofia", natomiast na potrzeby przykładów undergroundowych architektur zobaczymy tylko mały jej fragment. Przy okazji warto przypomnieć, że DDD aplikuje się raczej do złożonych problemów. Pytanie tylko, kto potrafi przewidzieć gdzie zabrnie projekt - już parę razy spotkałem się z projektami, które na początku wydawał się "szybką akcją bydgoskiej milicji". Nota bene: proste problemy powinno rozwiązywać się chyba w Excelu; ostatecznie w Accesie;) Ale jeżeli już czujemy silną wewnętrzną potrzebę aby wydurniać się z programowaniem prostych problemów i nie chcemy robić tego proceduralnie to zawsze można skorzystać z podejścia DDD Lite.

DDD tak na prawdę nie jest niczym nowym. To po prostu zestaw dobrych praktyk opartych na starych dobrych (ale nie tak starych i nie tak dobrych jak Turbo Pascal;) koncepcjach obiektowej analizy i projektowania. Wystarczy po prostu ich nie negować i nie programować wbrew nim - a jest to konieczne aby osiągnąć pewien cel...
DDD zakłada unifikację modelu analitycznego i projektowego, o którym już pisałem: UP-DDD in Action: Malutka Teoria Unifikacji. Cały zespół, począwszy od analityków, poprzez projektantów aż po koderów powinien posługiwać się wspólnym językiem opisującym domenę - zwanym w DDD Ubiquitous Language. Niestety aby było możliwe dosłowne zaprojektowanie pomysłów analityka musimy posługiwać się pełnym wachlarzem środków wyrazu języków OO. Procedury i encje nie wystarczą - o tym dalej przy okazji archetypów.

DDD zakłada modelowanie warstwy logiki domenowej przy użyciu pewnych standardowych klocków, zwanych Building Blocks. Jest to jedna z głównych koncepcji. Na pierwszy rzut oka może się wydawać, że jest ich całkiem dużo, ale każdy ma uzasadnienie swego istnienia wynikające z koncepcji Responsibility Driven Design, o której pisałem.

Artefakty już opisywałem, więc jedynie gwoli przypomnienia:
- Encje w DDD nie są tym samym czym czym w standardowym podejściu. Encje modelują biznes więc posiadają odpowiedzialność - metody biznesowe.
- Servisy w DDD nie są tym samym czym czym w standardowym podejściu. Serwisty z tej warstwy enkapsulują logikę, której nie można w sensowny sposób przypisać do odpowiedzialności żadnej z encji.
- Repozytorium jest abstrakcją persystencji - pamiętajmy, że w nietrywialnych projektach części agregatów (grafów encji) mogą pochodzić z różnych źródeł (np część z bazy, część z web servisu, a inna część z LDAP)
W ortodoksyjnym podejściu encje nie posiadają nawet getterów i setterów. To jest totalny underground w porównaniu z klasyką. Niewielu programistów potrafi to zaakceptować i dla tego chowają się za murem dysonansu kognitywnego.
Pewnie zastawiasz się: jeżeli nie ma nawet getterów/setterów to w jaki sposób wyświetlić encję na formularzu? Albo jak ją zmienić? Odpowiedź już niedaleko...

Nieograniczanie się do anemicznych (pozbawionych odpowiedzialności) encji oraz prymitywnych servisów ma kolejną niebagatelną zaletę. Posługiwanie się bogatym zestawem Building Blocks oraz korzystanie z bogactwa technik OO pozwala niemal dosłownie implementować zaawansowane i złożone modele analityczne. Na przykład modele wyprowadzone z archetypów. O archetypach już w niedalekiej przyszłości pojawię się parę postów na tym blogu, ponieważ IMHO jest to zdecydowanie ważniejsze niż kolejny gówniany framework webowy.
Póki co przyjrzyjmy się przykładowemu modelowi użytkowników na powyższym slajdzie... Piękny, prawda? Niestety nadaje się jedynie na przykłady w książkach lub jako model w projektach "Hello World".

Chcąc nieco bardziej profesjonalnie zaprojektować model np. użytkowników możemy
a) pomyśleć - co nie gwarantuje sukcesu
b) skorzystać z archetypu o nazwie "party" ("uczestnik", nie "impreza")
Tak jak wspomniałem o archetypach będzie później, ale zwróćmy uwagę na (pozorną) komplikację powyższego modelu. Jest fajny, ponieważ można w nim przedstawić dowolną relację pomiędzy dowolnymi uczestnikami (np ja mogę być klientem jakiejś firmy jak i jej pracownikiem; firma ta może być klientem mojej żony; itp).
Ale życzę powodzenia implementując ten (czy inny) zaawansowany archetyp przy pomocy klasycznego podejścia - anemicznego modelu. Widzicie już oczyma wyobraźni te łańcuszki 15 kropek i getterów - train wreck.
Z wykorzystaniem technik DDD możemy w sposób naturalny (OO) i bezproblemowy zaimplementować ten model a całą komplikację ukryć w hermetycznym agregacie (grafie encji), który operuje na swej wewnętrznej skomplikowanej strukturze poprzez metody biznesowe.

Undergroundowe w DDD jest również podejście do zależności. Otóż zależności na poziomie tej samej warstwy nie są niczym złym. Przecież zależności odzwierciedlają model biznesu, więc nie ma potrzeby aby się wydurniać z wstrzykiwaniem zależności czy tworzeniem na siłę interfejsu dla każdego komponentu biznesowego. Wiem, że niektóre platformy od niedawna;) obsługują Wstrzykiwanie Zależności i wymagają wszędzie interfejsów, ale do wszystkiego trzeba podejść z rozsądkiem.
Natomiast zależności modelu domenowego od klas infrastruktury są niepożądane. Nawet gdy klasy te są przykryte interfejsem - to nic nie zmienia, może co najwyżej poprawia nam samopoczucie.
Jedyna techniczną zależność modelu biznesowego jaką dopuszcza DDD jest zależność od szyny komunikatów. Obiekt biznesowy w momencie, gdy poczuje taką potrzebę może co najwyżej stworzyć komunikat i puścić go do pipe (która zawiera zwykle filtry). Jest to architektura pipeline.

Klasyczne podejście warstwowe również nie sprawdza się zbyt dobrze w przypadku DDD. Na powyższym slajdzie widzimy 2 możliwości:
Możemy "podpinać" obiekty biznesowe (te, które mają jakiś stan) pod GUI. Niestety wiąże się to z pewną niezręcznością: GUI "widzi" metody biznesowe. Poza tym obiekty biznesowe muszą oferować gettery i settery w celu dokonywania modyfikacji na nich. Niestety narusza to obiektowy paradygmat enkapsulacji, czyli prowadzi do pornografii obiektowej - odkrywania wewnętrznych struktur.
Podejście to ma jeszcze jedną wadę. Implementowanie setterów pozwala na istnienie obiektu w stanie niespójnym. Przykładowo zmieniając setterami encję Adres możemy zmienić miasto ale nie zmieniać kodu pocztowego:P
Mimo tych wad, podejście tego typu jest często stosowane w "prostych" przypadkach.
Strzałka po prawej stronie diagramy prezentuje inne podejście. W warstwie aplikacji obiekty biznesowe są przepakowywane do/z obiektów transportowych DTO. Wiem, że przepakowywanie obiektów kojarzy się z robotą szympansa, ale dzięki temu możemy uniknąć powyższych wad i niezręczności. Zaraz zaraz... jak pobrać stan obiektu biznesowego gdy nie ma on getterów? Myśl obiektowo; nie pytaj, wydawaj rozkazy! Oto rozwiązanie.
Podejście opierające się na przepakowywaniu ma jednak wadę polegającą na tym, że zupełnie niepotrzebnie pobieramy z repozytorium nadmierną ilość danych - ponieważ dany obiekt transferowy (potrzebny w danym przypadku użycia np. dla danego ekranu) może potrzebować znacznie mniej danych niż zawiera np agregat.

O ile powyższe podejścia do warstw są zadowalające, to prawdziwy underground pojawia się gdy uznamy, że jeden wymiary (wysokość warstw) nie wystarcza i potrzebujemy drugiego - heh to stary trik fizyków. Gdy brakuje im wymiarów aby upchać swe teorie to zakładają istnienie nowych -stąd w różnych odmianach Teorii Strun 11 a nawet 26 wymiarów czasu i przestrzeni;P
Ortodoksyjna architektura DDD wymaga istnienia 2 stosów. Jeden do "prymitywnych" operacji CRUD, drugi natomiast do obsługi rozkazów dla modelu domenowego. Podejście to zapewnia nam utrzymanie eleganckiego stylu Command-Query Separation. Stylu w którym polecenia odczytu stanu i polecenia zmiany stanu są jasno i przejrzyście odseparowane. Posiadanie osobnych klas manipulacyjnych stanem pozwala łatwo audytować i kontrolować ten kod.
Zauważmy, że stos DDD jest tylko do zapisu. Dokonujemy w nim zmian wysyłając komunikaty z poleceniami (wzorzec command). Obiekty biznesowe mogą ew. wygenerować swoje komunikaty i umieścić je w pipeline. Natomiast klient może odpytać o stan jedynie poprzez stos CRUD wysyłając komunikat z kwerendą.
Dodatkowo stos CRUD może zwracać Encje (nie biznesowe a anemiczne - czyli "normalne") będące obiektami transferowymi "szytymi" na miarę.
Separacja przetwarzania logiki od magazynu danych pozwala tworzyć architektury przygotowane na masowe obciążenie i skalowanie - DDDD (4xD to nie pomyłka, ale Distributed DDD). Architektury, gdzie liczy się coś więcej niż procedury i encje. Architektury, w których 2 stosy odpowiadają za osobne moduły z osobnymi magazynami danych w szczególności. Magazynami, które mogą być optymalizowane względem czasu wykonania (stos Command) i czasu wykonywania przekrojowych raportów (stos Query). Magazyny, które mogą asynchronicznie się komunikować i synchronizować.
Ale to temat na osobnego posta... Zainteresowanym polecam wywiad z Gregiem Youngiem - jednym z guru DDD.
//=========================
Tak wyglądają lekkie modyfikacje standardowych architektur warstwowych dopasowane do DDD i DDDD. W świecie Javy jest to jeszcze underground...
Ostatnią z prezentacji, która zakończyła moją sesję na 4developers była "Understanding Distributed Messaging Systems and Where They Fit" Raymonda Lewallena
ze ścieżki .NET.
Raymond opowiadał o podstawowych architekturach dużych i skalowalnych systemów rozproszonych opartych o komunikaty. Jakież było moje zdziwienie gdy Raymond wspomniał nagle coś o Agregacie, później o tym, że wysyła on komunikat do pipleina aż wreszcie zobaczyłem diagram architektury 2-stosowej. Dla niego było to takie oczywiste i naturalne, że używa DDD... nawet o tym nie wspomniał i nie robił żadnego wprowadzenia... po prostu standard:)
Chyba chłopaki od .NET wiedzą w co się bawić;P
Subskrybuj:
Posty (Atom)

