Prezentacja DDD

31 views
Skip to first unread message

Paweł Lipiński

unread,
Mar 17, 2009, 3:29:42 PM3/17/09
to warsza...@googlegroups.com
Hej
Dzisiejsza prezentacja z DDD dostępna jest pod adresem:
http://docs.google.com/Presentation?id=ddkr7r9_28ggnqq3dq

Już szykuje się ekipa ukraińców z Michałem Margielem na czele
czychająca na głowę Jacka Laskowskiego za nienagrywanie dzisiaj :)

Wszystkich, którym nie zdążyłem odpowiedzieć po prezentacji
przepraszam i zapraszam do dyskusji tu na forum.

pzdr
--
Paweł Lipiński
http://www.pawellipinski.com
http://blog.pawellipinski.com

Michal Margiel

unread,
Mar 17, 2009, 4:07:58 PM3/17/09
to warsza...@googlegroups.com


2009/3/17 Paweł Lipiński <lipinsk...@gmail.com>


Hej
Dzisiejsza prezentacja z DDD dostępna jest pod adresem:
http://docs.google.com/Presentation?id=ddkr7r9_28ggnqq3dq

Już szykuje się ekipa ukraińców z Michałem Margielem na czele
czychająca na głowę Jacka Laskowskiego za nienagrywanie dzisiaj :)

A żebyś wiedział! oberwie mu się za to!

Co do prezentacji no to sorry Paweł, że to napiszę ale była (za programem Wujek Dobra Rada)...

To wspaniała prezentacja Pawle,
Świetna prezentacja, bardzo dobra prezentacja,
Pycha!

Całkiem serio uważam, że zarówno merytorycznie jak i "szołmeńsko" było kapitalnie. Pokusiłbym się nawet, że była to jedna z najlepszych prezentacji w historii naszego juga.

Chyba z dobrą prezentacją jest tak jak z dobrym filmem - po obejrzeniu skłania nas on do przemyślenia pewnych spraw. I tak właśnie było w przypadku dzisiejszej prezentacji, i może w następnym projekcie uda nam się zastosować zasady przedstawione przez Pawła.

Dlatego tym bardziej jestem zawiedziony, że kamera się nie pojawiła.

Ps. w ramach statystyk to ja się naliczyłem 69 osób na prezentacji + 1 prowadzący, co stawia prezentacje o DDD w czołówce najchętniej wysłuchanych .

Jeszcze raz dzięki serdeczne i mam nadziej, że nie będziemy musieli długo czekać na Twój następny show :) 

--
Pozdrawiam/Best regards
Michał Margiel

http://www.linkedin.com/in/MichalMargiel
http://www.margiel.eu
http://poligloci.margiel.eu

Lukasz Lenart

unread,
Mar 17, 2009, 5:41:37 PM3/17/09
to warsza...@googlegroups.com
W dniu 17 marca 2009 20:29 użytkownik Paweł Lipiński
<lipinsk...@gmail.com> napisał:

> Dzisiejsza prezentacja z DDD dostępna jest pod adresem:
> http://docs.google.com/Presentation?id=ddkr7r9_28ggnqq3dq

Dzięki za prezentacje, do tej pory nie bardzo rozumiałem czym do końca
jest DDD i jak zwykle okazało się to proste i zrozumiałe (człowiek
zawsze boi się nieznanego ;-)

Sama idea modelu dziedziny, który jest czymś więcej niż zbiorem klas
mapowanych z bazy danych jest dla mnie jasna, jednak już cała otoczka
nie do końca, szczególnie pojęcie Serwisów. Jednak postaram się
pogłębić swoją wiedzę w tym temacie.

Trochę zabrakło mi "mięsa", jakiegoś prostego przykładu, takiego Hello
World DDD! Cóż będę musiał obejść się smakiem ;-) A może dasz się
namówić na powtórzenie prezentacji w ramach Warszawa DP?


Pozdrawiam
--
Lukasz
http://www.lenart.org.pl/

Jakub Nabrdalik

unread,
Mar 17, 2009, 6:58:11 PM3/17/09
to warsza...@googlegroups.com
Paweł Lipiński pisze:

> Wszystkich, którym nie zdążyłem odpowiedzieć po prezentacji
> przepraszam i zapraszam do dyskusji tu na forum.

Lukasz Lenart pisze:


> Trochę zabrakło mi "mięsa", jakiegoś prostego przykładu, takiego Hello
> World DDD!

Ja też się dołączam do marudzenia :)

Czytałem wcześniej artykuły na InfoQ, słuchałem mądrych ludzi na
wykładach i wszystko wygląda pięknie, ale chciałbym zobaczyć na własne
oczy działający kod.

Dasz się namówić na przykład (do ściągnięcia) prostej, webowej aplikacji
DDD opartej na Spring 2.5/3.0 + Hibernate? Takiej, która pokazuje:

1. jak wyszukać obiekty z poziomu serwisu skoro nie ma w nim
dao/repozytorium (dostęp do repozytorium tylko z warstwy domenowej)

2. do czego mają służyć fabryki o których wspomniałeś na wykładzie (na
poziomie domeny), skoro hibernate zapewni nam lazy loading

3. do czego mają nam służyć agregatory o których wspomniałeś na
wykładzie, skoro hibernate/jpa załatwia za nas agregację obiektów
domenowych w obiektach domenowych?

I żeby to rozwiązanie wynikało z Twoich doświadczeń z DDD (w sensie best
practices, a nie "byle-by-było-development").


Poza tym wykład bardzo przyjemny. Masz dar podobny do Jacka
Laskowskiego, który potrafi obudzić ludzi na ostatnich godzinach
całodniowej konferencji, kiedy kawa dawno przestała już działać ;)

Pozdrawiam,

Jakub

Maciej Zubala

unread,
Mar 18, 2009, 3:15:21 AM3/18/09
to warsza...@googlegroups.com
W dniu 17 marca 2009 23:58 użytkownik Jakub Nabrdalik
<jak...@gmail.com> napisał:

>
> Czytałem wcześniej artykuły na InfoQ, słuchałem mądrych ludzi na
> wykładach i wszystko wygląda pięknie, ale chciałbym zobaczyć na własne
> oczy działający kod.
>

Taki przykład jest dostępny w sieci:
http://dddsample.sourceforge.net/. Przy tworzeniu tego projektu brał
udział sam Eric Evans. Jest to kompletna aplikacja webowa w duchu DDD,
która ma dokładnie taką architekturę, jaką przedstawiał wczoraj Paweł.
Zastosowano w niej popularne obecnie narzędzia m.in. Spring,
Hibernate, ActiveMQ, co świadczy o tym, że DDD wcale się z nimi nie
"gryzie". Polecam obejrzenie tego kodu. Dla mnie był to pouczający
przykład.

Pozdrawiam

Maciek

Tomek Szymański

unread,
Mar 18, 2009, 3:29:53 AM3/18/09
to warsza...@googlegroups.com
A mnie sie podobalo szołmeńsko (choć czekałem tylko jak prowadzący
ogłosi, że zmienia imie na Evans ;-) ) i merytorycznie ale...

Chcialem zadac pytanie i mnie zakrzyczales i olales :-p

Zadam tutaj:

Bardzo niepodobal mi sie antyprzyklad z warstwami. IMO to bylo
dokladnie to samo, tyle ze zamiast Fasada bylo Serwis + Domena. No
nieslychanie duza zamiana ktora wywraca do gory nogami podejscie do
programowania ;-). Jak rozumiem problemem dla ciebie bylo w tamtym
przykladzie nawalanie klodem po kontrolerze, ale wcale nikt nie mowil,
ze sie tak powinno robic.

I tu zgadzam sie poniekad z kolega z tylu. W prezentacji brakowalo mi
konsekwencji. Antyprzykladem bylo programowanie neizgodne z
jakimikolwiek zasadami (jest jakis tam pattern, ale my nawet niego sie
nie trzymamy) a DDD pokazywalo jak to sie powinno robic dobrze - i
tutaj juz niby kazdy usiadzie i zadnego kodu nie napisze w
kontrolerze, w domenie nie wymsknie mu sie JDBC itp. itd.

Zreszta jak sie otworzy same obrazki to nie ma w nich nic zdroznego -
wymaga ten z lewej komentarza :].

No ale tak czy inaczej prezentacja fajna i generalnie DDD ladnie
zbiera pewne dorbe praktyki w calosc. Jak powiedzial mi Jacek K. po
prezentacji jest to taka rzecz, ktora jak sie czyta to czlowiek mysli
"no przeciez wiadomo, ze tak powinno byc", ale jak nie przeczyta, to
bedzie robil zle ;)

T.

Maciej Zubala

unread,
Mar 18, 2009, 3:42:15 AM3/18/09
to warsza...@googlegroups.com
W dniu 18 marca 2009 08:29 użytkownik Tomek Szymański
<szi...@szimano.org> napisał:

>
> Bardzo niepodobal mi sie antyprzyklad z warstwami. IMO to bylo
> dokladnie to samo, tyle ze zamiast Fasada bylo Serwis + Domena. No
> nieslychanie duza zamiana ktora wywraca do gory nogami podejscie do
> programowania  ;-). Jak rozumiem problemem dla ciebie bylo w tamtym
> przykladzie nawalanie klodem po kontrolerze, ale wcale nikt nie mowil,
> ze sie tak powinno robic.
>

Nie - problem polega na tym, że cała logika biznesowa jest w metodach
Fasady, co nie ma nic wspólnego z programowaniem obiektowym i do
złudzenia przypomina programowanie proceduralne w Pascalu. W tym
właściwym podejściu chodzi o to, że logika biznesowa jest realizowana
w warstwie domenowej przez obiekty, które łączą w sobie dane i
zachowania. Serwisy natomiast mają za zadanie jedynie pobrać obiekty
domenowe z repozytorium i zlecić im wykonanie logiki biznesowej.
Więcej o antywzorcu, który opisał Paweł możesz przeczytać choćby w
wikipedii ;) http://en.wikipedia.org/wiki/Anemic_Domain_Model albo u
Martina Fowlera, który jako pierwszy go opisał:
http://martinfowler.com/bliki/AnemicDomainModel.html.

Pozdrawiam

Maciek

Paweł Lipiński

unread,
Mar 18, 2009, 5:07:14 AM3/18/09
to warsza...@googlegroups.com
Luuudzie, ale się rozpisaliście!

To po kolei postaram się poodpowiadać:

---------------------
Lukasz Lenart:


> Sama idea modelu dziedziny, który jest czymś więcej niż zbiorem klas
> mapowanych z bazy danych jest dla mnie jasna, jednak już cała otoczka
> nie do końca, szczególnie pojęcie Serwisów. Jednak postaram się
> pogłębić swoją wiedzę w tym temacie.

No więc Łukaszu wyobraź sobie taką sytuację: robisz aplikację typu
CMS, są tam autorzy, artukuły (tu tytuł, data, treść, etc), działy
itp. I to wszystko ładnie możesz zamodelować dziedziną. A tu
przychodzi do Ciebie prośba o dorobienie RSS do tego. W tym RSSie ma
być nazwisko autora i tytuł artykułu. No to możesz jechać po
dziedzinie, ale to pewnie nie będzie zbyt wydajne, albo walnąć serwis,
który zwraca ten RSS pukając bezpośrednio przez repozytorium do bazy.
Pseudokod (więc nie czepiać się szczegółów):
public class ArticleRssService {
public List retrieveLastArticlesFromSection (Section section) {
List articles = articleRepository.findLastArticlesFromSection(section);
//jakieś przetworzenie listy do wersji wygodnej do wygenerowania RSSa
return resultingList;
}
}

public class ArticleRepository extends Repository {
public List findLastArticlesFromSection (Section section) {
return session
.createQuery("select a.title, a.author.name from Article a where
a.section=:section order by a.date desc")
.setParameter("section", section)
.setMaxResults(25)
.list();
}
}

Mógłbyś jednak woleć z jakiegoś powodu iść bezpośredio do dziedziny i
wtedy serwis będzie opakowywał wywołania jakichś metod dziedziny
(definiował jakiś algorytm dla nich) - np.:
public class ArticleRssService {
public List retrieveLastArticlesFromSection (Section section) {
List articles = articleRepository.findLastArticlesFromSection(section);
return createListOfRssEntries(articles);
}

private List createListOfRssEntries (List articles) {
List resultingList = new ArrayList(articles.size());
for (Article article : articles)
resultingList.add(article.returnAsRssEntry());
return resultingList;
}
}

Inny przykład dla serwisów to np.: redaktor w tym CMSie chce dodać
artykuł i opublikować go o 9:00 następnego dnia - ma do tego 1 klawisz
gdzieś w UI. Więc masz serwis:
public class ArticlePublishingService {
public void saveAndPublish(String articleName, String articleText,
String authorName, Date publishingDate) {
Article article = createArticleAggregate(articleName, articleText,
authorName);
articleRepository.save(article);
publisher.publish(article, publishingDate);
}

private Article createArticleAggregate(String articleName, String
articleText, String authorName) {
Article article = articleFactory.createArticle(articleName, articleText);
Author author = authorRepository.findByName(authorName);
author.setLastArticleDate(new Date());
article.setAuthor(author);
return article;
}
}
Z resztą ten publisher to pewnie też jakiś serwis, który jest wołany
np. z Quartza co minutę i publikuje wszystkie artykuły do
opublikowania w tym czasie.
To typowy przykład funkcjonalności nie za bardzo odpowiadającej dziedzinie.


Co do HelloWorld, to może coś napiszę jak znajdę czas w weekend, tym
bardziej, że jak widzę jest większe zapotrzebowanie na to :)
Co do DP meetingu, to też pewnie można by się dogadać.

---------------------
Jakub Nabrdalik:
1) Nie tylko nie mówiłem, że z serwisów nie powinno być dostępu do
repozytorium, ale nawet explicite powiedziałem, że jak najbardziej
powinien być (patrz mój przykład z CMSem dla Łukasza powyżej). Diagram
pokazywał warstwy a nie wszystkie możliwe zależności, i chyba po
prostu był mylący. Dostęp do repozytorium z serwisów jest zwykle
niezbędny (skąś w końcu trzeba te obiekty dziedziny na których
pracujemy uzyskać: albo z repozytorium jak już tam są, albo z fabryki
jeśli tworzymy nowe).

2) Fabryki są do tworzenia nowych obiektów dziedziny i powinny być
używane wyłącznie jeśli jest taka potrzeba (tzn konstrukcja tych
obiektów jest złożona - powiedzmy heurystycznie, że więcej niż 2
linijki). Głównie dotyczy to więc agregatów. Tak jak mówiłem, fabryki
powstają zwykle jako skutek refaktoryzacji 'extract method' gdy
tworzenie nowych obiektów/agregatów jest złożone.

3) Agregaty mają nam ułatwić zarządzanie grafami encji tak, by nie
było niezbędne za każdym razem zarządzanie każdą encją oddzielnie. DDD
nie jest związane z hibernatem, więc nie zakłada istnienia takich
mechanizmów. Ale skoro już je mamy, to część sprawy jest załatwiona
(przynajmniej relacje i cascading). Tak czy siak można sobie wyobrazić
sytuację, w której różne zestawy encji o wspólnym ich podzbiorze muszą
być zarządzane wspólnie (tzn każdy taki agregat oddzielnie) i
statyczne deklarowanie sposobu ładowania i relacji w hibernacie może
nie wystarczać. Np. w jednym widoku w aplikacji potrzebujemy danych
autor 1-* artykuł *-* sekcja
a w innym
autor *-1 miasto 1-filia_firmy
Wspólny jest tu tylko autor. Nie chcemy ładować artykułów autora przy
wyszukiwaniu wszystkich autorów z jakiegoś miasta, ani czytelnika
artykułu nie interesuje (a więc do jego widoku nie powinno być
ładowane) w jakiej filii naszej firmy dany autor jest zatrudniony.
Stąd różne agregaty mimo wspólnego podzbioru encji.

---------------------
Tomek Szymański:
Do odpowiedzi Maćka dodam jeszcze jedno: rzuć okiem na kod powyżej
(ten dla Łukasza) i porównaj go z bardziej popularną/klasyczną wersją
(tak, jestem tu trochę tendencyjny):

@Session
public class ArticlePublishingBean {
public void saveAndPublish(String articleName, String articleText,
String authorName, Date publishingDate) {
Article ar = new Article(articleName, articleText);
Author au = em.createQuery("from Author a where
a.name=:name).setParameter("name",authorName).uniqueResult();
ar.setAuthor(au);
em.persist(ar);
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
Scheduler sched = schedFact.getScheduler();
sched.start();
JobDetail jobDetail = new JobDetail("publisher", null,
ArticlePublisher.class);
Trigger trigger = TriggerUtils.makeMinutelyTrigger("publishTrigger");
sched.scheduleJob(jobDetail, trigger);
}
}

Kod powyżej jest bardzo typowy dla większości aplikacji - robi wiele
rzeczy w jednej metodzie (w końcu to fasada), nie ma rozróżnienia
poziomu abstrakcji operacji. Jest po prostu płaski - typowo
proceduralny i do tego nieporozbijany na drobniejsze metody. Sam
refactoring w postaci wydzielenia schedulowania, tworzenia encji i
zapisu do oddzielnych metod wiele nie poprawi, bo dalej klasa będzie
miała wiele różnych odpowiedzialności (patrz wpis na moim blogu na
temat Single Responsibility Principle dot. klas). Jak pododajesz
jeszcze 5-6 takich funkcjonalności, klasa będzie miała 25 różnych
metod a po roku przestanie być utrzymywalna. W przypadku podziału
warstw odpowiedzialności są rozłożone warstwami (odpowiednio: serwisy,
dziedzina, repozytorium) i ten problem znika.

------------------

A tak w ogóle to dziękuję za miłe przyjęcie i recenzje :)

pozdrawiam

Tomek Szymański

unread,
Mar 18, 2009, 5:18:34 AM3/18/09
to warsza...@googlegroups.com
> Tomek Szymański:
> Do odpowiedzi Maćka dodam jeszcze jedno: rzuć okiem na kod powyżej
> (ten dla Łukasza) i porównaj go z bardziej popularną/klasyczną wersją
> (tak, jestem tu trochę tendencyjny):

Ale mnie chodzi tylko o to, ze robienie plaskich fasad jest Złe. Nikt
tak komu nie kazał pisać, ani nikt tak nikogo nie uczył (chyba, że ja
byłem w innych szkołach :-p ). A przedstawiałeś to tak jakby do 17
Marca 2009 17:59 wszystkie autorytety na niebie i ziemi (poza Evansem
of cors) uczyly, ze nalezy robic plaskie fasady zaraz za kontrolerem,
ktore nawalaja po DAO.

Takie moje 3gr.

T.

Lukasz Lenart

unread,
Mar 18, 2009, 5:28:10 AM3/18/09
to warsza...@googlegroups.com
W dniu 18 marca 2009 10:07 użytkownik Paweł Lipiński
<lipinsk...@gmail.com> napisał:
> Luuudzie, ale się rozpisaliście!

To nie trzeba było rozbudzać apetytów ;-)

> [ciach]

Jednak się przyczepię, czy nie lepiej jednak by było upchnąć taką
metodę w dziedzinie (Section.getArticlesAsRSS() ?), która wywołuje
metody ArticleRssService? Chodzi mi o ten konkretny przypadek, wtedy
UI, klient, etc. nie ma pojęcia o serwisie.

> Inny przykład dla serwisów to np.: redaktor w tym CMSie chce dodać

> [ciach]

To bardziej do mnie przemawia i w sumie racja, nie jest to element
dziedziny i powinno być robione obok. Tak czułem, że oto chodzi i
tylko potrzebowałem potwierdzenia ;-)

> Co do HelloWorld, to może coś napiszę jak znajdę czas w weekend, tym
> bardziej, że jak widzę jest większe zapotrzebowanie na to :)

Ten przykład, który ktoś podał DDSample jak dla mnie będzie ok, więc
nie musisz się męczyć.

> Co do DP meetingu, to też pewnie można by się dogadać.

Oczywiście, preferujemy dyskusję i kodowanie nad prezentację, czy też
pokazanie jak to się robi, tak jak było ostatnio z SOLID - rewelacja.
Teraz przymierzamy się doi czegoś w stylo Randori Session czy Coding
Dojo.

Paweł Lipiński

unread,
Mar 18, 2009, 5:31:17 AM3/18/09
to warsza...@googlegroups.com
Tomek, ja nie mówiłem o żadnych autorytetach tylko o rzeczywistości.
Tak wygląda większość aplikacji niezależnie czy tego chcesz czy nie,
czy tak jest dobrze czy nie i czy tak mówią autorytety czy nie. Ten
wykład to nie mój doktorat ani pana Evansa, który miałby zawierać
pomysły rewolucjonizujące tworzenie oprogramowania, tylko mały wkład w
podniesienie średniej jakości oprogramowania na naszej planecie.

Ja wiem, że na JUGu to może jest oczywiste, bo tam przychodzą ludzie
zainteresowani rozwijaniem swoich umiejętności, wiedzy itp. Ale
rzeczywistość większości aplikacji jest taka jaka jest i tylko przez
szerzenie (może nawet oczywistych, ale jakoś nieczęsto praktykowanych)
technik i idei można coś tu poprawić.

2009/3/18 Tomek Szymański <szi...@szimano.org>:

Adam Warski

unread,
Mar 18, 2009, 5:33:17 AM3/18/09
to warsza...@googlegroups.com
Cześć,

na wstępie - prezentacja bardzo dobra :). Ale faktycznie przydałby się
chyba jakiś ciąg dalszy z przykładami :)

Co do samego DDD, to nie za bardzo rozumiem jaka logika miałaby się
znaleźć w dziedzinie. Z Twojego przykładu jak i na moje oko większość
logiki będzie jednak w serwisach. Rzadko kiedy (tak przynajmniej
wynika z mojego małego doświadczenia) "prawdziwa" logika dotyczy tylko
jednej encji/ obiektu dziedziny: raczej spina pare różnych. A wtedy
trzeba ją umieścić w serwisach, a nie w dziedzinie, bo przecież logika
dziedzinowa może operować tylko na jednym obiekcie swojego typu.

(Nie miałem jeszcze czasu obejrzeć przykładowego programu, może to mi
trochę wyjaśni.)

Swoją drogą, z tego co rozumiem to guru DDD (Evans itd) są
zwolennikami rich domain, a nie anemic domain. Ale nigdy nie doszedłem
do tego jak oni chcą to pogodzić z single responsibility? Jak będziemy
dodawać różne funkcjonalności do klas domeny, to w końcu klasa ta,
poza trzymaniem danych, będzie miała 50 niezwiązanych ze sobą metod,
robiących kompletnie różne rzeczy?

Adam

Paweł Lipiński

unread,
Mar 18, 2009, 5:37:23 AM3/18/09
to warsza...@googlegroups.com
Łukasz Lenart napisał:

> Jednak się przyczepię, czy nie lepiej jednak by było upchnąć taką
> metodę w dziedzinie (Section.getArticlesAsRSS() ?), która wywołuje
> metody ArticleRssService? Chodzi mi o ten konkretny przypadek, wtedy
> UI, klient, etc. nie ma pojęcia o serwisie.

I w ten sposób łamiesz konwencję JavaBean i tymsamym udowadniasz, że
nie masz racji :-P
Dziedzina jest warstwowo poniżej serwisów więc nie ma prawa się do
nich dobierać. Nie chodzi mi tu o ideologię, tylko prostą zasadę, że
zależności tylko "w dół" która zapewnia odpowiedni podział poziomów
abstrakcji i zwiększa czytelność kodu.
W jaki sposób dostaniesz te artykuły (czy przez relację z sekcją, czy
bezp. zapytaniem) ma mniejsze znaczenie i tu faktycznie można by
pewnie dyskutować.

Paweł Lipiński

unread,
Mar 18, 2009, 5:48:08 AM3/18/09
to warsza...@googlegroups.com
2009/3/18 Adam Warski <ad...@warski.org>:

> Z Twojego przykładu jak i na moje oko większość
> logiki będzie jednak w serwisach.
Bo przykład jest strywializowany. Ale częściowo masz rację. Serwisy są
proceduralne, bo realizują pewien algorytm. Dziedzina jest obiektowa,
bo realizuje relacje i operacje na samych tych obiektach.

> Rzadko kiedy (tak przynajmniej
> wynika z mojego małego doświadczenia) "prawdziwa" logika dotyczy tylko
> jednej encji/ obiektu dziedziny: raczej spina pare różnych. A wtedy
> trzeba ją umieścić w serwisach, a nie w dziedzinie, bo przecież logika
> dziedzinowa może operować tylko na jednym obiekcie swojego typu.

Nie zgodzę się z ostatnim zdaniem. Logika dziedzinowa może dotyczyć
obiektów które są w relacji z 'this'. Tzn. może wywoływać ich metody.
Idealnie oczywiście tylko metody obiektów będących w bezpośredniej
relacji (a te zagłębiają się dalej).
Serwisy (choć to trochę zależnie od technologii) wchodzą wtedy gdy
operacje dotyczą nieobiektowych zachowań albo dotyczą niepowiązanych
ze sobą obiektów (artykuł <-> publisher w przykładzie powyżej)

> Swoją drogą, z tego co rozumiem to guru DDD (Evans itd) są
> zwolennikami rich domain, a nie anemic domain. Ale nigdy nie doszedłem
> do tego jak oni chcą to pogodzić z single responsibility? Jak będziemy
> dodawać różne funkcjonalności do klas domeny, to w końcu klasa ta,
> poza trzymaniem danych, będzie miała 50 niezwiązanych ze sobą metod,
> robiących kompletnie różne rzeczy?

Nie rozumiem chyba pytania. SRP nie jest w sprzeczności z
obiektowością (zwaną tu rich domain) tylko jest regułą jej realizacji.
Jeśli twój obiekt ma tak wiele zachowań, to powinny być one
umieszczone w nim właśnie. One nie będą ze sobą 'niezwiązane', wręcz
odwrotnie wszystkie są związane przynależnością do tego samego
obiektu. To że umiesz chodzić i pić nie oznacza, że musisz być 2
różnymi osobami. SRP w przypadku klas dotyczy tylko tego, żeby klasa
zawierała metody faktycznie do niej przynależące, czyli klasa Article
ma metody dotyczące artykułu a nie np. wysyłania maili z
potwierdzeniem opublikowania.

pzdr

Lukasz Lenart

unread,
Mar 18, 2009, 5:52:59 AM3/18/09
to warsza...@googlegroups.com
W dniu 18 marca 2009 10:37 użytkownik Paweł Lipiński
<lipinsk...@gmail.com> napisał:

> I w ten sposób łamiesz konwencję JavaBean i tymsamym udowadniasz, że
> nie masz racji :-P

Znaczy, że co, że nie można tego edytować za pomocą wizualnych edytorów?
“A Java Bean is a reusable software component that can be manipulated visually
in a builder tool.”

A jeśli chodzi o nazwę metody to listArticlesAsRSS() będzie lepsze po
nie rozumiem.

> Dziedzina jest warstwowo poniżej serwisów więc nie ma prawa się do
> nich dobierać. Nie chodzi mi tu o ideologię, tylko prostą zasadę, że
> zależności tylko "w dół" która zapewnia odpowiedni podział poziomów
> abstrakcji i zwiększa czytelność kodu.

Czyli serwisy operują na dziedzinie, która to jest zbiorem POJO? Czyli
tak zwany anemiczny model? Czegoś tu nie rozumiem ;-(

> W jaki sposób dostaniesz te artykuły (czy przez relację z sekcją, czy
> bezp. zapytaniem) ma mniejsze znaczenie i tu faktycznie można by
> pewnie dyskutować.

W twoim przykładzie przekazałeś Section do serwisu i dla mnie było
jasne, że Section zawiera listę powiązanych z nią artykułów i w sumie
teraz tak sobie myślę, że ten serwis jest zbędny. Klasa Article ma
metodę returnAsRssEntry(), pozostaje tylko wybrać odpowiednią listę
tych artykułów (najnowsze 10) co załatwi repozytorium.

Jakub Nabrdalik

unread,
Mar 18, 2009, 6:29:57 AM3/18/09
to warsza...@googlegroups.com
Paweł Lipiński pisze:

> ---------------------
> Jakub Nabrdalik:
> 1) Nie tylko nie mówiłem, że z serwisów nie powinno być dostępu do
> repozytorium, ale nawet explicite powiedziałem, że jak najbardziej
> powinien być (patrz mój przykład z CMSem dla Łukasza powyżej). Diagram
> pokazywał warstwy a nie wszystkie możliwe zależności, i chyba po
> prostu był mylący. Dostęp do repozytorium z serwisów jest zwykle
> niezbędny (skąś w końcu trzeba te obiekty dziedziny na których
> pracujemy uzyskać: albo z repozytorium jak już tam są, albo z fabryki
> jeśli tworzymy nowe).

Dzięki, to mi bardzo rozjaśniło. Wychodziłem z założenia, że tylko
obiekty domenowe mają dostęp do repozytoriów/dao, ale w sumie faktycznie
lepiej żeby istniały podstawowe interfejsy wyciągania obiektów
implementowane przez repozytoria i wykorzystywane przez serwisy.
"Podstawowe" bo dam sobie rękę uciąć, że gdyby znalazło się tam za dużo
metod (save/persist, delete etc.) któryś z programistów w zespole
zacznie z powrotem umieszczać logikę w serwisach.


> 2) Fabryki są do tworzenia nowych obiektów dziedziny i powinny być
> używane wyłącznie jeśli jest taka potrzeba (tzn konstrukcja tych
> obiektów jest złożona - powiedzmy heurystycznie, że więcej niż 2
> linijki). Głównie dotyczy to więc agregatów. Tak jak mówiłem, fabryki
> powstają zwykle jako skutek refaktoryzacji 'extract method' gdy
> tworzenie nowych obiektów/agregatów jest złożone.

Ok, teraz ogarniam. Od kilku lat unikam fabryk jak ognia - kojarzą mi
się z programowanie z przed ery IoC, ale w opisanych przez Ciebie
sytuacjach mają sens.


> 3) Agregaty mają nam ułatwić zarządzanie grafami encji tak, by nie
> było niezbędne za każdym razem zarządzanie każdą encją oddzielnie. DDD
> nie jest związane z hibernatem, więc nie zakłada istnienia takich
> mechanizmów. Ale skoro już je mamy, to część sprawy jest załatwiona
> (przynajmniej relacje i cascading). Tak czy siak można sobie wyobrazić
> sytuację, w której różne zestawy encji o wspólnym ich podzbiorze muszą
> być zarządzane wspólnie (tzn każdy taki agregat oddzielnie) i
> statyczne deklarowanie sposobu ładowania i relacji w hibernacie może
> nie wystarczać. Np. w jednym widoku w aplikacji potrzebujemy danych
> autor 1-* artykuł *-* sekcja
> a w innym
> autor *-1 miasto 1-filia_firmy
> Wspólny jest tu tylko autor. Nie chcemy ładować artykułów autora przy
> wyszukiwaniu wszystkich autorów z jakiegoś miasta, ani czytelnika
> artykułu nie interesuje (a więc do jego widoku nie powinno być
> ładowane) w jakiej filii naszej firmy dany autor jest zatrudniony.
> Stąd różne agregaty mimo wspólnego podzbioru encji.

To również rozjaśnia . Dodaj te trzy punkty do swojej prezentacji, gdy
będziesz znów gdzieś występował - bardzo się przydadzą.

Raz jeszcze dzięki :)

Jakub

Paweł Lipiński

unread,
Mar 18, 2009, 7:17:09 AM3/18/09
to warsza...@googlegroups.com
> Znaczy, że co, że nie można tego edytować za pomocą wizualnych edytorów?
> "A Java Bean is a reusable software component that can be manipulated visually
> in a builder tool."
>
> A jeśli chodzi o nazwę metody to listArticlesAsRSS() będzie lepsze po
> nie rozumiem.
No oczywiście, że się tej nazwy czepnąłem. Ale jasne, że to tylko
przykład, stąd ta emotikonka przy moim komentarzu.

> Czyli serwisy operują na dziedzinie, która to jest zbiorem POJO? Czyli
> tak zwany anemiczny model? Czegoś tu nie rozumiem ;-(

Zaraz zaraz. Anemiczny model to brak modelu, ew. obiekty jako
struktury danych bez zachowań. Serwisy operują tu na pełnych
obiektach, czyli tzw. rich model.

> W twoim przykładzie przekazałeś Section do serwisu i dla mnie było
> jasne, że Section zawiera listę powiązanych z nią artykułów i w sumie
> teraz tak sobie myślę, że ten serwis jest zbędny. Klasa Article ma
> metodę returnAsRssEntry(), pozostaje tylko wybrać odpowiednią listę
> tych artykułów (najnowsze 10) co załatwi repozytorium.

To ja tylko zaznaczę, że kod był wymyślony ad hoc i walnięty prosto do
maila, więc te szczegóły są zupełnie nieistotne.
Wersja z returnAsRssEntry wewn. klasy Article jest ok. dla 10
elementów. Jeśli miałbyś ich zwrócić 10 000 to byś zamordował serwer.
Wtedy wali się z serwisu prosto przez repozytorium odpowiednim
zapytaniem tylko po wybrane dane i się je bez materializowania w
obiekty zwraca do wrzucenia do RSSa. Przerabiałem więc wiem :)
To z resztą dobry przykład zarówno na refactoring architektury (ta
sama funkcjonalność innym sposobem) jak i na użycie serwisu z
pominięciem dziedziny.

Lukasz Lenart

unread,
Mar 18, 2009, 7:29:25 AM3/18/09
to warsza...@googlegroups.com
W dniu 18 marca 2009 12:17 użytkownik Paweł Lipiński
<lipinsk...@gmail.com> napisał:

>> A jeśli chodzi o nazwę metody to listArticlesAsRSS() będzie lepsze po
>> nie rozumiem.

> No oczywiście, że się tej nazwy czepnąłem. Ale jasne, że to tylko
> przykład, stąd ta emotikonka przy moim komentarzu.

już się bałem, że do reszty zgłupiałem :P

> [ciach]

Ok, wszystko jasne, czyli to jest to o czym zawsze myślałem a nie
zawsze udało mi się wdrożyć ;-)

Adam Warski

unread,
Mar 19, 2009, 2:45:19 AM3/19/09
to warsza...@googlegroups.com
Cześć,


>> Rzadko kiedy (tak przynajmniej
>> wynika z mojego małego doświadczenia) "prawdziwa" logika dotyczy
>> tylko
>> jednej encji/ obiektu dziedziny: raczej spina pare różnych. A wtedy
>> trzeba ją umieścić w serwisach, a nie w dziedzinie, bo przecież
>> logika
>> dziedzinowa może operować tylko na jednym obiekcie swojego typu.
> Nie zgodzę się z ostatnim zdaniem. Logika dziedzinowa może dotyczyć
> obiektów które są w relacji z 'this'. Tzn. może wywoływać ich metody.
> Idealnie oczywiście tylko metody obiektów będących w bezpośredniej
> relacji (a te zagłębiają się dalej).

Tak, racja.

> Serwisy (choć to trochę zależnie od technologii) wchodzą wtedy gdy
> operacje dotyczą nieobiektowych zachowań albo dotyczą niepowiązanych
> ze sobą obiektów (artykuł <-> publisher w przykładzie powyżej)
>
>> Swoją drogą, z tego co rozumiem to guru DDD (Evans itd) są
>> zwolennikami rich domain, a nie anemic domain. Ale nigdy nie
>> doszedłem
>> do tego jak oni chcą to pogodzić z single responsibility? Jak
>> będziemy
>> dodawać różne funkcjonalności do klas domeny, to w końcu klasa ta,
>> poza trzymaniem danych, będzie miała 50 niezwiązanych ze sobą metod,
>> robiących kompletnie różne rzeczy?
> Nie rozumiem chyba pytania. SRP nie jest w sprzeczności z
> obiektowością (zwaną tu rich domain) tylko jest regułą jej realizacji.
> Jeśli twój obiekt ma tak wiele zachowań, to powinny być one
> umieszczone w nim właśnie. One nie będą ze sobą 'niezwiązane', wręcz
> odwrotnie wszystkie są związane przynależnością do tego samego
> obiektu. To że umiesz chodzić i pić nie oznacza, że musisz być 2
> różnymi osobami. SRP w przypadku klas dotyczy tylko tego, żeby klasa
> zawierała metody faktycznie do niej przynależące, czyli klasa Article
> ma metody dotyczące artykułu a nie np. wysyłania maili z
> potwierdzeniem opublikowania.

Obiektowość a rich domain to chyba jednak co innego. Nie widzę
sprzeczności SRP z obiektowością, tylko właśnie z rich domain. Nie
podoba mi się umieszanie wszystkich (lub prawie wszystkich) zachowań
obiektów w jednej klasie. Jeżeli mam np. konwersję artykułów do RSSa i
do PDFa, to jak dla mnie to powinno być w osobnych klasach.

Ale to pewnie też w dużej mierze kwestia gustu - ja chyba wolę
traktować dane "dziedzinowe" bardziej jakby były typami algebraicznymi
(czy jak to się tłumaczy: algebraic data type, czyli pattern matching
i te sprawy). Obiektowość oczywiście też, ale gdzie indziej, i nie
wszędzie :). Chociaż często w Javie tego się nie da elegancko załatwić
- ale w innych językach może już być lepiej, jak mamy choćby pattern
matching w scali czy extension methods).

Co do analogii z człowiekiem, to człowieka się ani nie rozszerza o
nowe funkcje, ani nie modyfikuje istniejących zbyt często ;)

Adam

Adam Lider

unread,
Mar 19, 2009, 3:33:25 AM3/19/09
to warsza...@googlegroups.com
Witam,
ciekawy temat i prezentacja. mam tylko male pytanie jak realizujesz
dostep do repozytoriow z poziomu obiektow modelu zakladajac ze uzywamy
Springa do zarzadzania lifecyclem fasad, serwisow, rezpozytoriow, a
Hibernate jako mechanizm persistence dla obiektow modelu, czyli dosc
standarodowo. Pytam bo choc to szczegol techonologiczny to ciezko bylo
of ladne rozwiazanie pare lat temu, gdy mialem przyjemosc tworzyc
takie rozwiazania na platformie java/jee.

On Mar 18, 2009, at 10:07 AM, Paweł Lipiński wrote:

> Jakub Nabrdalik:
> 1) Nie tylko nie mówiłem, że z serwisów nie powinno być dostępu do
> repozytorium, ale nawet explicite powiedziałem, że jak najbardziej
> powinien być (patrz mój przykład z CMSem dla Łukasza powyżej). Diagram
> pokazywał warstwy a nie wszystkie możliwe zależności, i chyba po
> prostu był mylący. Dostęp do repozytorium z serwisów jest zwykle
> niezbędny (skąś w końcu trzeba te obiekty dziedziny na których
> pracujemy uzyskać: albo z repozytorium jak już tam są, albo z fabryki
> jeśli tworzymy nowe).


Adam Lider


Paweł Lipiński

unread,
Mar 19, 2009, 4:04:26 AM3/19/09
to warsza...@googlegroups.com
> Obiektowość a rich domain to chyba jednak co innego. Nie widzę
> sprzeczności SRP z obiektowością, tylko właśnie z rich domain. Nie
> podoba mi się umieszanie wszystkich (lub prawie wszystkich) zachowań
> obiektów w jednej klasie. Jeżeli mam np. konwersję artykułów do RSSa i
> do PDFa, to jak dla mnie to powinno być w osobnych klasach.
No dobra, nie chodziło mi o to, że to to samo, ale że rich domain jest
realizacją obiektowości w aplikacjach biznesowych. I dlatego nie widzę
sprzeczności między SRP a rich domain. W Twoim przykładzie (konwersja
do RSS i PDF) żadna z tych metod nie powinna być w Article, bo to
pogwałca SRP. Ale również do jest smell pt. 'feature envy' - klasa
chce robić za dużo. Za konwersję odpowiedzialny powinien być konwerter
(tam gdzieś wcześniej pisałem też o wysyłaniu maili notyfikacyjnych o
powstaniu artykułu, co również dotyczy Article, ale nie powinno w niej
być umieszczone).
Dlatego ja bym specjalnie nie rozróżniał między obiektowością a rich domain.

Paweł Lipiński

unread,
Mar 19, 2009, 4:09:18 AM3/19/09
to warsza...@googlegroups.com
2009/3/19 Adam Lider <adam....@googlemail.com>:

> Witam,
>        ciekawy temat i prezentacja. mam tylko male pytanie jak realizujesz
> dostep do repozytoriow z poziomu obiektow modelu zakladajac ze uzywamy
> Springa do zarzadzania lifecyclem fasad, serwisow, rezpozytoriow, a
> Hibernate jako mechanizm persistence dla obiektow modelu, czyli dosc
> standarodowo. Pytam bo choc to szczegol techonologiczny to ciezko bylo
> of ladne rozwiazanie pare lat temu, gdy mialem przyjemosc tworzyc
> takie rozwiazania na platformie java/jee.

Teraz w Springu (od v2.5 o ile pamiętam) jest już trochę łatwiej, bo
można obiekty dziedzinowe zadnotować jako @Configurable i wszczepić w
nie co się chce.
Wcześniej załatwiałem to ręcznie za pomocą aspectj'a.

Adam Warski

unread,
Mar 19, 2009, 8:05:58 AM3/19/09
to warsza...@googlegroups.com
Witam,

>> Obiektowość a rich domain to chyba jednak co innego. Nie widzę
>> sprzeczności SRP z obiektowością, tylko właśnie z rich domain. Nie
>> podoba mi się umieszanie wszystkich (lub prawie wszystkich) zachowań
>> obiektów w jednej klasie. Jeżeli mam np. konwersję artykułów do
>> RSSa i
>> do PDFa, to jak dla mnie to powinno być w osobnych klasach.
> No dobra, nie chodziło mi o to, że to to samo, ale że rich domain jest
> realizacją obiektowości w aplikacjach biznesowych. I dlatego nie widzę
> sprzeczności między SRP a rich domain. W Twoim przykładzie (konwersja
> do RSS i PDF) żadna z tych metod nie powinna być w Article, bo to
> pogwałca SRP.

Ok, to się nawet zgadzamy :)

> Ale również do jest smell pt. 'feature envy' - klasa
> chce robić za dużo. Za konwersję odpowiedzialny powinien być konwerter
> (tam gdzieś wcześniej pisałem też o wysyłaniu maili notyfikacyjnych o
> powstaniu artykułu, co również dotyczy Article, ale nie powinno w niej
> być umieszczone).

I wtedy Article wie jak stworzyć konwerter? Ew. fabrykę konwerterów?
Czy serwis?

To czego za bardzo nie rozumiem w DDD, to rozdzielenie logiki na dwa
miejsca: serwisy i obiekty dziedziny. I tego właśnie dotyczą moje
przykłady - nie za bardzo wiem, jaka logika miałaby się znajdować w
dziedzinie.

Pozdrowienia,
Adam

Paweł Lipiński

unread,
Mar 19, 2009, 9:09:13 AM3/19/09
to warsza...@googlegroups.com
2009/3/19 Adam Warski <ad...@warski.org>:

> To czego za bardzo nie rozumiem w DDD, to rozdzielenie logiki na dwa
> miejsca: serwisy i obiekty dziedziny. I tego właśnie dotyczą moje
> przykłady - nie za bardzo wiem, jaka logika miałaby się znajdować w
> dziedzinie.
No to od innej strony:
To jest taka różnica jak między testami jednostkowymi i
funkcjonalnymi. Tak +/-: testy jednostkowe testują logikę dziedziny,
testy funkcjonalne na poziomie serwisów.

Weźmy ten wymęczony przeze mnie przykład z CMSem. Masz artykuły, one
mają autorów, czytelników, którzy mogą zostawiać komentarze itp. Teraz
w klasie Article będą pola typu tytuł, treść, data dodania, data
publikacji, etc. Metody będą zarządzały relacjami (typu
dodajKomentarz(Komentarz k)) ale również realizowały funkcjonalności
na poziomie pojedynczego artykułu. Np.:
- countStatistics() licząca słowa, zdania, itp (np. na potrzeby
określenia płacy autora)
- abstract() zwracająca skrót artykułu (np. tytuł + pierwsze 10 zdań)
- language() zwracająca język artykułu np. na podst. naiwnego
klasyfikatora bayesowskiego
itp itd.
I teraz jeśli będziesz miał różne typy artykułów (zwykły tekst,
fotorelacja, itp) to mogą być różne implementacje tych metod - w
przypadku umieszczenia ich w jakimś serwisie musiałbyś mnożyć serwisy
albo wstawiać w tych metodach if (article.getType() == Article.TEXT)
{...} else ... i podobne kwiatki (blueeee)

Serwisy realizują wtedy funkcjonalności bardziej end-2-end. Np.:
chcesz na raz zapisać artykuł, wrzucić go do publishera a jego
statystyki i język do hurtowni danych.

Już jaśniejsze, czy ciągle tylko mącę?

Jakub Nabrdalik

unread,
Mar 19, 2009, 9:47:02 AM3/19/09
to warsza...@googlegroups.com
Adam Warski pisze:

> To czego za bardzo nie rozumiem w DDD, to rozdzielenie logiki na dwa
> miejsca: serwisy i obiekty dziedziny. I tego właśnie dotyczą moje
> przykłady - nie za bardzo wiem, jaka logika miałaby się znajdować w
> dziedzinie.

Rozgryzam właśnie przykład dddsample i nasuwa mi się odpowiedź. Paweł,
wypowiedz się proszę, czy to ma sens, czy dobrze to rozumiem, czy też
pieprzę głupoty.


Przykład wyróżnia dwa rodzaje serwisów: te należące do domeny i te
należące do aplikacji (wzorzec fasady).

To co wystawia funkcje biznesowe dla warstwy view (controller + view) to
SERWISY APLIKACJI (wzorzec fasady i NIC więcej; żadnej logiki).

W SERWISACH DZIEDZINY siedzi część logiki biznesowej (Te serwisy należą
do dziedziny)


Pytanie było, która część (jaka logika) leży w serwisach dziedziny?

Oto odpowiedź:

"Domain services encapsulate domain concepts that just are not naturally
modeled as things."

Jeśli fragment logiki biznesowej (np. rejestracja użytkownika) pasuje do
jakiejś klasy dziedziny, należy ją tam koniecznie umieścić
(user.register()).

Jeśli nie pasuje do żadnej klasy dziedziny, należy ją wrzucić w serwis
dziedziny (userService.deleteNotActivatedUsers()).

Ma to sens?

Jakub

Paweł Lipiński

unread,
Mar 19, 2009, 10:17:25 AM3/19/09
to warsza...@googlegroups.com
> Jeśli fragment logiki biznesowej (np. rejestracja użytkownika) pasuje do
> jakiejś klasy dziedziny, należy ją tam koniecznie umieścić
> (user.register()).
>
> Jeśli nie pasuje do żadnej klasy dziedziny, należy ją wrzucić w serwis
> dziedziny (userService.deleteNotActivatedUsers()).

Gdybyś był słuchał dokładnie (mówiłem prawie dokładnie takie zdanie,
tylko po polsku) i śledził ten wątek (przykłady dla Łukasza i Adama),
wszystko było by już jasne :-P

Oczywiście, że jak coś pasuje do dziedziny to powinno być w
dziedzinie. Generalna zasada jest taka, że w serwisach powinno znaleźć
się to co nie pasuje do żadnego obiektu dziedziny ani nie sposób
zrobić dla tego rozsądnego nowego (czyli dokładnie to, czego się nie
modeluje jako obiekty, kod proceduralny), albo to co definiuje jakiś
"algorytm" na obiektach dziedziny (aka fasada) - zbiera do kupy jakieś
operacje obiektowe.

Cytat za Domain-Driven Design Quickly:
A Service should not replace the operation which normally
belongs on domain objects. We should not create a Service for
every operation needed. But when such an operation stands out
as an important concept in the domain, a Service should be
created for it. There are three characteristics of a Service:
1. The operation performed by the Service refers to a domain
concept which does not naturally belong to an Entity or Value
Object.
2. The operation performed refers to other objects in the domain.
3. The operation is stateless.


Co do twojego przykładu user.register() to raczej register nie powinno
być na User, bo user nie zawiera rejestru w którym się rejestruje.
Raczej pewnie coś w rodzaju usersRegister.register(user).
To jest właśnie jeden z typowych elementów refactoringu w DDD i w
ogóle modelowaniu obiektowym - zmienianie położenia metod tak, by
maksymalnie odpowiadały one odpowiedzialnościom klas (tu rejestrowanie
prawie na pewno nie jest odpowiedzialnością usera).

Maciej Zubala

unread,
Mar 19, 2009, 10:31:40 AM3/19/09
to warsza...@googlegroups.com
W dniu 19 marca 2009 15:17 użytkownik Paweł Lipiński
<lipinsk...@gmail.com> napisał:
>

> Gdybyś był słuchał dokładnie (mówiłem prawie dokładnie takie zdanie,
> tylko po polsku) i śledził ten wątek (przykłady dla Łukasza i Adama),
> wszystko było by już jasne :-P
>
> Oczywiście, że jak coś pasuje do dziedziny to powinno być w
> dziedzinie. Generalna zasada jest taka, że w serwisach powinno znaleźć
> się to co nie pasuje do żadnego obiektu dziedziny ani nie sposób
> zrobić dla tego rozsądnego nowego (czyli dokładnie to, czego się nie
> modeluje jako obiekty, kod proceduralny), albo to co definiuje jakiś
> "algorytm" na obiektach dziedziny (aka fasada) - zbiera do kupy jakieś
> operacje obiektowe.

Wydaje mi się, że nie mówiłeś o rozróżnieniu serwisów aplikacyjnych
(warstwa aplikacji) od serwisów domenowych (warstwa dziedziny), stąd
te wszystkie wątpliwości. Serwisy domenowe są podobnie jak encje,
value objecty, agregaty i repozytoria obiektami dziedziny. Wykonują
one logikę biznesową, która w naturalny sposób nie pasuje do encji czy
value objectów. Oprócz serwisów domenowych są jeszcze serwisy
aplikacyjne, których zadaniem jest tylko delegowanie wykonania logiki
biznesowej do bytów z warstwy domenowej (serwisy domenowe, encje,
agregaty, vo) w celu wykonania poszczególnych use casów. Dzięki
zastosowaniu serwisów aplikacyjnych zmniejsza się złożoność warstwy
ui.

Jakub Nabrdalik

unread,
Mar 19, 2009, 10:40:10 AM3/19/09
to warsza...@googlegroups.com
Paweł Lipiński pisze:

> Gdybyś był słuchał dokładnie (mówiłem prawie dokładnie takie zdanie,
> tylko po polsku) i śledził ten wątek (przykłady dla Łukasza i Adama),
> wszystko było by już jasne :-P

No wiesz... Kumam że wolno kumam, ale też zrozumienie nie polega na
przeczytaniu, tylko przetrawieniu tak by wyprodukować wyniki samemu :)
Moje wysłałem do weryfikacji.

> Co do twojego przykładu user.register() to raczej register nie powinno
> być na User, bo user nie zawiera rejestru w którym się rejestruje.
> Raczej pewnie coś w rodzaju usersRegister.register(user).
> To jest właśnie jeden z typowych elementów refactoringu w DDD i w
> ogóle modelowaniu obiektowym - zmienianie położenia metod tak, by
> maksymalnie odpowiadały one odpowiedzialnościom klas (tu rejestrowanie
> prawie na pewno nie jest odpowiedzialnością usera).

Dlaczego User nie zawiera (nie ma wstrzykiwanego) rejestru w którym się
rejestruje?

Dla przykładu: student wpisuje SIĘ na listę, a nie lista wpisuje
studenta. Odpowiedzialnością studenta jest wpisanie się na listę.

Or am I missing something completely?

Jakub

Paweł Lipiński

unread,
Mar 19, 2009, 10:51:18 AM3/19/09
to warsza...@googlegroups.com
> Dlaczego User nie zawiera (nie ma wstrzykiwanego) rejestru w którym się
> rejestruje?
>
> Dla przykładu: student wpisuje SIĘ na listę, a nie lista wpisuje
> studenta. Odpowiedzialnością studenta jest wpisanie się na listę.
>
> Or am I missing something completely?

Jakub, to już są takie szczegóły, że trudno tu o ogólne rozwiązanie
(tudzież poprawną odpowiedź). Może być i tak i tak, to zależy od
konkretnego przypadku.
Akurat ze studentem to nie koniecznie tak jest, bo może być tak, że
lista ma dodatkową logikę która na to nie pozwoli (max liczba
studentów itp.) Ale może też być tak, że faktycznie to leży w
odpowiedzialnościach usera.
Generalnie chodziło mi o to, że częścią DDD jest właśnie rozważanie
takich subtelności, bo one mają wpływ na jakość designu a więc
utrzymywalność, czytelność itp.


A tak w ogóle to są niuanse. W wykładzie nie chodziło o przedstawienie
wszystkiego co wyprodukowano o DDD, tylko o podrzucenie tematu (do
zgłębienia samemu), zwrócenie uwagi na ważność modelowania
obiektowego, dbanie o jakość designu.
Bardzo się cieszę, że ta dyskusja się tak rozwlekła, bo to wskazuje na
zainteresowanie tematem i chęć Poszukiwania Prawdy w naszej braci
developerskiej ;)

Paweł Lipiński

unread,
Mar 19, 2009, 10:59:42 AM3/19/09
to warsza...@googlegroups.com
> Wydaje mi się, że nie mówiłeś o rozróżnieniu serwisów aplikacyjnych
> (warstwa aplikacji) od serwisów domenowych (warstwa dziedziny), stąd
> te wszystkie wątpliwości.
Faktycznie nie rozróżniłem ich jako dwa oddzielne wzorce (z resztą O
ILE PAMIĘTAM Evans też ich nie rozróżnia, tylko pisze o tych
dwóch/trzech głównych przypadkach użycia serwisów).
Ale faktycznie może nie wystarczająco jasno przedstawiłem rozróżnienie.

> Serwisy domenowe są podobnie jak encje,
> value objecty, agregaty i repozytoria obiektami dziedziny.

Z oczywistych względów repozytoria nie są obiektami dziedziny (tylko
infrastruktury). Co do tych serwisów domenowych, to też bym dyskutował
(dla mnie wszystkie serwisy to tzw warstwa aplikacyjna), ale to jest
prawie zupełnie bez znaczenia. Ważne żeby dobrze wyczuć co jest
obiektowe a co serwisowe.

> Wykonują
> one logikę biznesową, która w naturalny sposób nie pasuje do encji czy
> value objectów. Oprócz serwisów domenowych są jeszcze serwisy
> aplikacyjne, których zadaniem jest tylko delegowanie wykonania logiki
> biznesowej do bytów z warstwy domenowej (serwisy domenowe, encje,
> agregaty, vo) w celu wykonania poszczególnych use casów.

No tzw. serwisy aplikacyjne to raczej nie delegują do tzw serwisów
domenowych, bo to by sensu za dużo nie miało (chyba, że rozróżnienie
fizyczne warstw jako sztuka dla sztuki)

> Dzięki
> zastosowaniu serwisów aplikacyjnych zmniejsza się złożoność warstwy
> ui.

No to już nie prawda niestety. Serwisy powinny być niezależne od UI,
więc nie powinny zmniejszać jej złożoności (zależności warstwami tylko
w dół).
Chyba, że chodzi Ci o to, że w serwisie jest teraz to, co bywało w
kontrolerze (czyli de facto jakaś logika aplikacji), ale to nie jest
zmniejszanie złożoności tylko poprawny design (a przynajmniej taka
jest motywacja, choć efektywnie oczywiście zmniejsza to złożoność
kontrolera).

Jakub Nabrdalik

unread,
Mar 19, 2009, 11:56:09 AM3/19/09
to warsza...@googlegroups.com
Paweł Lipiński pisze:

>> Wykonują
>> one logikę biznesową, która w naturalny sposób nie pasuje do encji czy
>> value objectów. Oprócz serwisów domenowych są jeszcze serwisy
>> aplikacyjne, których zadaniem jest tylko delegowanie wykonania logiki
>> biznesowej do bytów z warstwy domenowej (serwisy domenowe, encje,
>> agregaty, vo) w celu wykonania poszczególnych use casów.
> No tzw. serwisy aplikacyjne to raczej nie delegują do tzw serwisów
> domenowych, bo to by sensu za dużo nie miało (chyba, że rozróżnienie
> fizyczne warstw jako sztuka dla sztuki)

Tu się z Evansem nie zgadzasz (zakładając że faktycznie maczał palce w
przykładzie).

Z przykładu dddsample: klasa BookingServiceFacadeImpl (serwis
aplikacyjny) wykorzystuje BookingService (serwis domenowy)

public String registerNewCargo(String origin, String destination) {
TrackingId trackingId = bookingService.bookNewCargo(new
UnLocode(origin), new UnLocode(destination));
return trackingId.idString();
}

Dla mnie ma to głęboki sens. Serwisy aplikacyjne mówią o tym co nasza
aplikacja udostępnia światu (czyli dla kontrolera i view, odpowiednik
interakcji z użytkownikiem i systemami zewnętrznymi w use case'ach),
natomiast serwisy dziedzinowe mówią o tym jakie operacje, poza tymi
dostępnymi w obiektach dziedzinowych, istnieją na całej dziedzinie.

IMHO, to nie kwestia estetyki, ale faktyczny podział odpowiedzialności.

>> Dzięki
>> zastosowaniu serwisów aplikacyjnych zmniejsza się złożoność warstwy
>> ui.
> No to już nie prawda niestety.

> [ciach]

Zgadzam się z Pawłem. Niezależnie czy używamy DDD czy nie, w kontrolerze
powinna znajdować się jedynie logika prezentacji, która nigdy nie trafia
do serwisów.

Jakub

Adam Warski

unread,
Mar 19, 2009, 1:20:26 PM3/19/09
to warsza...@googlegroups.com
Witam,

> No to od innej strony:
> To jest taka różnica jak między testami jednostkowymi i
> funkcjonalnymi. Tak +/-: testy jednostkowe testują logikę dziedziny,
> testy funkcjonalne na poziomie serwisów.

> (...)


> Serwisy realizują wtedy funkcjonalności bardziej end-2-end. Np.:
> chcesz na raz zapisać artykuł, wrzucić go do publishera a jego
> statystyki i język do hurtowni danych.
>
> Już jaśniejsze, czy ciągle tylko mącę?

Tak, rozumiem o co chodzi - ale moje oryginalne "ale" zostaje bez
rozwiązania. Zgadzam się że dobrze wykorzystać obiektowość w przypadku
logiki "dziedzinowej", i w żadnym przypadku nie uważam że rzeczy typu
"if (article.getType() == Article.TEXT)" są ładne/ poprawne/ itd.
Wątpiliwości mam bardziej ideologiczne:
- jeżeli jest dużo logiki dziedzinowej to klasa dziedziny będzie
przeładowana, łamiąc choćby SRP i podobne
- czy naprawdę chcemy rozbijać logikę na dwie warstwy? nie jestem
przekonany; serwisy też mogą przecież korzystać z obiektowości, a
metody obiektów dziedziny mogą mieć "algorytmy" zapisane bardziej
strukturalnie

Przypuszczam że musiałbym napisać jakiś większy projekt w DDD żeby
zobaczyć jak to w praktyce wygląda. Nie mam niestety żadnych dobrych
rozwiązań tych problemów, ale jakby co, to dam znać ;). Czasami
chciałoby się "dopisać" metodę do hierarchii klas i zgrupować kod
takich metod w jednym miejscu - np. kod konwertujący artykuły do
jakiegoś formatu. To by rozwiązało pierwszą wątpliwość, bo logikę
dziedzinową możnaby podzielić między pare "klas". W Javie można coś
podonego czasami zaimplementować za pomocą visitorów. Jest też
propozycja "extension methods" (które chyba są w .NET), a w Scali mamy
implicit konwersje, za pomocą których można uzyskać podobny efekt. Są
też traity, w których możemy zawrzeć wspólne funkcjonalności i potem
używać ich w wielu klasach. Tak więc gdzieś tam jest odpowiedź ;)

Pozdrowienia,
Adam

Maciej Zubala

unread,
Mar 19, 2009, 5:28:13 PM3/19/09
to warsza...@googlegroups.com
W dniu 19 marca 2009 15:59 użytkownik Paweł Lipiński
<lipinsk...@gmail.com> napisał:
>

> Faktycznie nie rozróżniłem ich jako dwa oddzielne wzorce (z resztą O
> ILE PAMIĘTAM Evans też ich nie rozróżnia, tylko pisze o tych
> dwóch/trzech głównych przypadkach użycia serwisów).
> Ale faktycznie może nie wystarczająco jasno przedstawiłem rozróżnienie.

Ależ oczywiście, że rozróżnia. Nie jestem w stanie w tej chwili
zacytować odpowiednich rozdziałów, bo nie mam książki przed oczyma ;).
Implementując model posługujemy się następującymi bytami (Evans nazywa
je building blocks):
- encje,
- value objects,
- agregaty,
- fabryki,
- repozytoria,
- serwisy

Wszystkie te building blocks składają się na wastwę dziedziny.
Dodatkowo zaleca się przykrycie warstwy dziedziny warstwą aplikacji,
na którą składają się serwisy aplikacyjne. Do wykonania poszczególnych
use casów serwisy aplikacyjne używają tych wyżej wymienionych building
blocks. Dodatkowo realizują odpowiedzialności, których nie można
określić mianem logiki biznesowej, np. transakcje, bezpieczeństwo,
przechowywanie stanu use casów, czyli rzeczy ściśle techniczne, które
nie są znane ekspertom biznesowym.

> Z oczywistych względów repozytoria nie są obiektami dziedziny (tylko
> infrastruktury).

Z tym zdaniem zgadzam się, ale tylko w połowie ;) Analizując kod
aplikacji dddsample, do której linka gdzieś już w tej dyskusji
wkleiłem, widzimy, że w warstwie dziedziny znajdują się jedynie
interfejsy repozytoriów. Implementacje możemy natomiast znaleźć w
warstwie infrastruktury. Większość repozytoriów ma tam 2
implementacje. Jedna hibernetowa, a druga in-memory na potrzeby unit
testów.

> Co do tych serwisów domenowych, to też bym dyskutował
> (dla mnie wszystkie serwisy to tzw warstwa aplikacyjna), ale to jest
> prawie zupełnie bez znaczenia. Ważne żeby dobrze wyczuć co jest
> obiektowe a co serwisowe.

> No tzw. serwisy aplikacyjne to raczej nie delegują do tzw serwisów
> domenowych, bo to by sensu za dużo nie miało (chyba, że rozróżnienie
> fizyczne warstw jako sztuka dla sztuki)

Jak napisałem wyżej serwisy aplikacyjne to co innego niż serwisy
domenowe. Leżą w odrębnych warstwach i mają różne zadania. Główna
różnica jest taka, że aplikacyjne nie wykonują logiki biznesowej,
tylko jak sama nazwa wskazuje logikę aplikacji, czyli zaczynają/kończą
transakcje, sprawdzają czy dany user ma w ogóle prawo wykonać daną
metodę itp. Domenowe wykonują logikę biznesową, której nie dało się za
bardzo włożyć do encji/value objectów. Może podam przykład, żeby było
wiadomo o co mi chodzi:

AccountService {

@Transactional
void transferFunds(String sourceAccountNumber, String
destinationAccountNumber, double ammount) {
Account source =
accountRepository.getAccountByNumber(sourceAccountNumber);
Account destination =
accountRepository.getAccountByNumber(destinationAccount);
fundsTransferService.makeTransfer(source, destination, ammount);
}

}

AccountService, to serwis z warstwy aplikacji. Posiada metodę
transferFunds, która pobiera odpowiednie konta z repozytorium. Na
etapie modelowania zdecydowano, że wykonywanie przelewów to nie jest
odpowiedzialność konta (nie ma metody Account.transfer(Account source,
double money)), więc wprowadzono serwis domenowy, który się tym
zajmuje.

> No to już nie prawda niestety. Serwisy powinny być niezależne od UI,
> więc nie powinny zmniejszać jej złożoności (zależności warstwami tylko
> w dół).
> Chyba, że chodzi Ci o to, że w serwisie jest teraz to, co bywało w
> kontrolerze (czyli de facto jakaś logika aplikacji), ale to nie jest
> zmniejszanie złożoności tylko poprawny design (a przynajmniej taka
> jest motywacja, choć efektywnie oczywiście zmniejsza to złożoność
> kontrolera).
>

Tak właśnie o to mi chodziło. Gdyby nie serwisy aplikacyjne, to
musielibyśmy używać obiektów domenowych w warstwie ui, co zwiększyłoby
poziom komplikacji ui zwłaszcza jeśli dorzucilibyśmy transakcje,
autoryzacje itd. Dzięki warstwie aplikacji warstwa ui faktycznie
odpowiada tylko za wyświetlanie danych. Nie jest jednak powiedziane,
że zawsze należy wprowadzać warstwę aplikacji w celu odzielenia
dziedziny od ui. W przypadku mniej złożonych dziedzin/mniejszych
projektów być może warto na poziomie ui posługiwać się obiektami
biznesowymi - nowa warstwa to zawsze więcej pracy.

Paweł Lipiński

unread,
Mar 20, 2009, 5:27:32 AM3/20/09
to warsza...@googlegroups.com
Maćku, bardzo to ładnie wyłożyłeś :) - z mojego punktu widzenia
najważniejsze są jednak ostatnie zdania. Na architekturę ma wpływ
użyta technologia. Gdy masz dostęp bezpośrednio do dziedziny z UI
(grails, seam, etc) ta warstwowość się trochę rozmywa. Z drugiej
strony czasem warto jest dołożyć warstwę "dla zasady" - jako wyrażenie
explicite założeń architektonicznych. Nie robiąc wyjątków od reguł
zwracamy uwagę, że są one ważne i że kładziemy nacisk na ich
stosowanie. W końcu programowanie jako działanie zespołowe podlega
normalnym prawom psychologii.

pozdrawiam

Sławek Sóbotka

unread,
Apr 6, 2009, 2:39:04 AM4/6/09
to Warszawa Java User Group (Warszawa JUG)
Z tymi servisami nie jest tak prosto...

1. Mozna mowic o roznych rodzajach servisow
http://jonathan-oliver.blogspot.com/2008/12/services-infrastructure-application.html

btw: Co do servisow apliakcyjnych jako fasady to zwykle tak jest. Ale
ja podchodze *czasem* do takiego servisu jak do Use Case. UC jest
klasą a jego kroki są metodami. UC jest z definicji stanowy.

2. Nie nalezy automatycznie "doklejac" metod biznesowych do encji
nawet gdy mają tam sens poniewaz istnieją 2 aspekty, ktore trzeba brac
pod uwage aby ZNOWU nie zrobic syfu:
- bounded context
- rodzaje domen: core, supporting oraz generic
http://www.infoq.com/presentations/strategic-design-evans


--
Sławek Sobótka
http://art-of-software.blogspot.com
Reply all
Reply to author
Forward
0 new messages