Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Przykład DDD

Witam,

tworzę sobie przykładową aplikację w metodyce DDD. Chciałem się zapytać czy sensownie zdekomponowałem problem na elementy domeny (entities, value objects, services) ?

Tematem aplikacji są relacje z meczów piłkarskich. Przykładem czegoś takiego w akcji jest serwis http://nazywo.interia.pl/ . Jak widać mamy tam listę meczów, których relację chcemy oglądać (a raczej czytać). Kiedy wejdziemy w jakaś relację z meczu piłkarskiego, widzimy listę zdarzeń na boisku wg. czasu wraz z opisem. Do tego możemy sobie oglądnąć wszelkiego rodzaju podsumowania czy statystki oraz składy obu drużyn. Oprócz relacji z pojedynczych meczów, aplikacja ma pokazywać również zbiorcze zestawienia takie jak np na http://wyniki.interia.pl/ .Czyli wszelkiego rodzaju tabelki ligowe, które można eksplorować wg. sezonów i rodzaju rozgrywek. Dla uproszczenia założyłem, że przewidujemy rozgrywki tylko dla jednego kraju. Poza tym oprócz rzeczy związanych z rozgrywkami, chciałbym prezentować informacje nt. zawodników grających w danym klubie.

Moja dekompozycja na elementy składowe domeny:

Entities:

sezon
rozgrywki
mecz
klub
zawodnik
kontrakt

Value Objects:

wzrost,
waga,
data,
adres,
zdarzenie meczowe,

Services:

rejestr zawodników (realizuje transfery między klubami)

Ciekawy jestem, jak ktoś inny popatrzy na ten temat. Mam cichą nadzieje, na konstruktywną burzę mózgów ;)
Jarosław Żeliński

Jarosław Żeliński Analityk i
Projektant Systemów

Temat: Przykład DDD

model dziedziny w DDD to klasy a nie encje... ;), po drugie bez wiedzy o tym czy są to agregaty czy proste asocjacje i jak są skojarzona obiekty Value z pozostałymi nadal nic nie wiadomo :)

jaką "wartością" jest "zdarzenie meczowe"?

co to za usługa "rejestr zawodników? Czy to jakiś kontener?
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Przykład DDD

Rejestr zawodników pomyślałem jako usługę, która pozwala związać jakiegoś zawodnika z jakimś klubem poprzez kontrakt. Posiadała by metody w rodzaju "zarejestrujZawodnika(zawodnik, klub)", która zawodnikowi bez kontraktu przypisywała by kontrakt z danym klubem. Metodę "transferujZawodnika(klubZ,klubDo, zawodnik)", która by zakańczała kontrakt z jednym klubem i tworzyła z nowym. Kontrakt to prosta klasa mająca w zasadzie trzy pola - zawodnik, klub, czyAktualny.

Co do zdarzenia meczowego, to właśnie cały czas się zastanawiam czy powinna być to wartość czy raczej encja. Generalnie jeżeli zdarzenie składa się z pól (czas, treść) to mając dwa zdarzenia o tych samych wartościach tych pól, to można by powiedzieć, że to jedno zdarzenie. Z jednym ale - każde z nich mogło wystąpić w innym meczu, jak sobie tak to przemyślę, to jednak chyba zrobiłbym z tego encje :).

Jeżeli chodzi o agregaty to generalnie nie mam zbytniej koncepcji jak je stworzyć (wstępnie może bym widział je jako sezon-rozgrywki i mecz-zdarzenieMeczowe), z drugiej strony mam wrażenie, że może wystarczyły by proste asocjacje, może jakaś mała podpowiedź ?

Co do VO to wzrost, waga i data na pewno będą używane w encji Zawodnik. Adres w encji Klub. W meczu również używana będzie data.

Tak się zastanawiam, może najlepiej było by zrobić jakiś uml-owy diagram klas ?
Adam Brodziak

Adam Brodziak PHP, football, fun

Temat: Przykład DDD

Zdarzenie meczowe jest encją. Gol strzelony przez Rooneya w 60min MC jest czymś innym niż gol z tej samej minuty w meczu z Wolves.

Co do transferów to proponuję dodatkową relację M:N - członkostwo - która zawierałaby referencję do klubu i zawodnika a także date rozpoczęcia i zakończenia na przykład. Tak ralacja staje się osobną encją.
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Przykład DDD

Adam Brodziak:
Zdarzenie meczowe jest encją. Gol strzelony przez Rooneya w 60min MC jest czymś innym niż gol z tej samej minuty w meczu z Wolves.

Co do transferów to proponuję dodatkową relację M:N - członkostwo - która zawierałaby referencję do klubu i zawodnika a także date rozpoczęcia i zakończenia na przykład. Tak ralacja staje się osobną encją.

Moją encję "członkowstwo" nazwałem "kontraktem". Jest to chyba bardziej adekwatna nazwa. Natomiast ciekawi mnie jak rozwiązać kwestię sezonów i rozgrywek. Czy któraś z tych encji powinna być agregatem, czy raczej lepsze będzie dwukierunkowa relacja między sezonem a rozgrywkami (n:m) ?
Adam Brodziak

Adam Brodziak PHP, football, fun

Temat: Przykład DDD

Jak miałaby wyglądać ralacja sezon - rozgrywki M:N? IMHO sezon przynależy do rozgrywek, nawet nazewnictwo na to wskazuje, np. Serie A 2010/2011. Ten sezon nie ma nic wspólnego z La Liga 2010/2011.
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Przykład DDD

Adam Brodziak:
Jak miałaby wyglądać ralacja sezon - rozgrywki M:N? IMHO sezon przynależy do rozgrywek, nawet nazewnictwo na to wskazuje, np. Serie A 2010/2011. Ten sezon nie ma nic wspólnego z La Liga 2010/2011.

Ja to widzę tak, że mając sezon dla jednego kraju, w ramach niego odbywają się rozgrywki. Sezon determinuje czas ich trwania. Po zakończeniu sezonu jakiegoś kończą się też rozgrywki poszczególne. Czyli mamy obiekt klasy sezon - "Sezon 2010/11", który ma w sobie listę rozgrywek, która każda z nich jest obiektem klasy 'rozgrywka' czy jak to inaczej tam nazwać. Natomiast w klasie 'rozgrywka' (czy lepiej może zawody) jest pole sezon w którym będzie obiekt klasy sezon.
Jarosław Żeliński

Jarosław Żeliński Analityk i
Projektant Systemów

Temat: Przykład DDD

Mi wychodzi, że :):
- potrzebna jest klasa skupiająca wiedzę o planowaniu rozgrywek, ona tworzy agregat Sezon zawierający mecze (każdy ma w atrybutach kto z kim i kiedy)
- są klasy Klub zawierające zawodników (agregat)
- wszelkie umowy to zarządzane przez kontener (Umowy) obiekty (klasa Konkretna Umowa) zawierające dane o stronach umowy i jej rodzaju oraz kompletną treść umowy (np. jej skan)
- nie widze potrzeby tworzenia trwałych relacji (nie licząc agregatów), np. sprawdzenie jaką aktualną i z kim ma umowę zawodnik łatwo wykona kontener zawierający umowy na podstawie np. nazwiska (lub innego identyfikatora) zawodnika.
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Przykład DDD

Jarek Żeliński:
Mi wychodzi, że :):
- potrzebna jest klasa skupiająca wiedzę o planowaniu rozgrywek, ona tworzy agregat Sezon zawierający mecze (każdy ma w atrybutach kto z kim i kiedy)
- są klasy Klub zawierające zawodników (agregat)
- wszelkie umowy to zarządzane przez kontener (Umowy) obiekty (klasa Konkretna Umowa) zawierające dane o stronach umowy i jej rodzaju oraz kompletną treść umowy (np. jej skan)
- nie widze potrzeby tworzenia trwałych relacji (nie licząc agregatów), np. sprawdzenie jaką aktualną i z kim ma umowę zawodnik łatwo wykona kontener zawierający umowy na podstawie np. nazwiska (lub innego identyfikatora) zawodnika.

Ok, w takim razie flow działania będzie wyglądał mniej więcej tak (o ile dobrze rozumiem):

zawodnik:
klub tworzy nowy obiekt zawodnika i obiekt umowy, w którym jedna stroną jest klub a drugą zawodnika, umowa jest przekazywana do kontenera umów

mecze:
sezon tworzy nowy mecz dla jakiś rozgrywek

To co powyżej wydaje się dość jasne. Natomiast jak widzisz dodawanie nowych zdarzeń do meczu i wyciąganie danych do różnych tabel ligowych i innych podsumowań ?
Jarosław Żeliński

Jarosław Żeliński Analityk i
Projektant Systemów

Temat: Przykład DDD

Wojciech Soczyński:
To co powyżej wydaje się dość jasne. Natomiast jak widzisz dodawanie nowych zdarzeń do meczu i wyciąganie danych do różnych tabel ligowych i innych podsumowań ?

idziemy w dodawanie przypadków użycia :), ale:
- o jakie "nowe zdarzenia w meczu chodzi"?
- mecze wiedzą wszystko o sobie, konkretny mecz ma poza datą także jego wynik
- wydaje mi się, że wszelkie tabele wyników itp. powinien sporządzać stosowny Sezon, jeżeli potrzebna jest wiedza za lata to potrzebna jest klasa odpowiedzialna za wiedzę o Sezonach np. LigaPiłkarska

Brniemy w "projekt", być może powinna pojawić się tablica historii meczy zarządzana przez LigęPiłkarską, wtedy harmonogram rozgrywek będzie zarządzany przez Sezon ale historia wyników będzie osobnym "miejscem". Wtedy bezpiecznie obiekty Mecz będą zawierały tylko jedną datę planowaną (ta może się zmieniać) zaś historia "zdarzeń które się wydarzyły" była by osobną kolekcją obiektów reprezentując historyczne zdarzenia.

Obawiam się, że bez modelu (klasy i sekwencje) nie pojedziemy za daleko bez ryzyka "utraty spójności" :)
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Przykład DDD

Jarek Żeliński:
Wojciech Soczyński:
To co powyżej wydaje się dość jasne. Natomiast jak widzisz dodawanie nowych zdarzeń do meczu i wyciąganie danych do różnych tabel ligowych i innych podsumowań ?

idziemy w dodawanie przypadków użycia :), ale:
- o jakie "nowe zdarzenia w meczu chodzi"?
- mecze wiedzą wszystko o sobie, konkretny mecz ma poza datą także jego wynik
- wydaje mi się, że wszelkie tabele wyników itp. powinien sporządzać stosowny Sezon, jeżeli potrzebna jest wiedza za lata to potrzebna jest klasa odpowiedzialna za wiedzę o Sezonach np. LigaPiłkarska

Brniemy w "projekt", być może powinna pojawić się tablica historii meczy zarządzana przez LigęPiłkarską, wtedy harmonogram rozgrywek będzie zarządzany przez Sezon ale historia wyników będzie osobnym "miejscem". Wtedy bezpiecznie obiekty Mecz będą zawierały tylko jedną datę planowaną (ta może się zmieniać) zaś historia "zdarzeń które się wydarzyły" była by osobną kolekcją obiektów reprezentując historyczne zdarzenia.

Obawiam się, że bez modelu (klasy i sekwencje) nie pojedziemy za daleko bez ryzyka "utraty spójności" :)

Zdarzenie - event meczowy potrzebny do sporządzania relacji na żywo z meczów (przykład relacji: http://nazywo.interia.pl/relacja/wisla-krakow-legia-wa.... Również mi się zdaję, że przydały by się jakieś diagramy, tak dla jasności, bo w tej chwili jeszcze do końca nie ogarniam twojej wizji ;)
Jarosław Żeliński

Jarosław Żeliński Analityk i
Projektant Systemów

Temat: Przykład DDD

Wojciech Soczyński:
Obawiam się, że bez modelu (klasy i sekwencje) nie pojedziemy za daleko bez ryzyka "utraty spójności" :)

Zdarzenie - event meczowy potrzebny do sporządzania relacji na żywo z meczów (przykład relacji: http://nazywo.interia.pl/relacja/wisla-krakow-legia-wa.... Również mi się zdaję, że przydały by się jakieś diagramy, tak dla jasności, bo w tej chwili jeszcze do końca nie ogarniam twojej wizji ;)

wizje mam dość ogólna jednak ważne są np. granice systemu i aktorzy, link jaki podałeś nieco zmienia postać rzeczy :) i nie jest to trywialny system. Ten event meczowy moim zdaniem to element agregatu "Przebieg meczu", tu chyba miejsce na metodę "Dodaj zdarzenie" oraz oraz metodę "Pokaż chronologicznie zdarzenia".
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Przykład DDD

Jarek Żeliński:
Wojciech Soczyński:
Obawiam się, że bez modelu (klasy i sekwencje) nie pojedziemy za daleko bez ryzyka "utraty spójności" :)

Zdarzenie - event meczowy potrzebny do sporządzania relacji na żywo z meczów (przykład relacji: http://nazywo.interia.pl/relacja/wisla-krakow-legia-wa.... Również mi się zdaję, że przydały by się jakieś diagramy, tak dla jasności, bo w tej chwili jeszcze do końca nie ogarniam twojej wizji ;)

wizje mam dość ogólna jednak ważne są np. granice systemu i aktorzy, link jaki podałeś nieco zmienia postać rzeczy :) i nie jest to trywialny system. Ten event meczowy moim zdaniem to element agregatu "Przebieg meczu", tu chyba miejsce na metodę "Dodaj zdarzenie" oraz oraz metodę "Pokaż chronologicznie zdarzenia".
Dlatego właśnie, że nie jest to trywialny system, uważam, że to dobry materiał na DDD. Z drugiej strony nie jest jeszcze na tyle skomplikowany, żeby jego implementacja miała zająć dużo czasu.

Jeżeli chodzi o te relacje live, to sądzę, że to co napisałeś (agregat przebieg meczu, metoda dodaj zdarzenie i pokaz chronologicznie zdarzenia) wystarczy, by zbudować na tym całość relacji tj. najważniejsze wydarzenia, podsumowanie czy inne statystyki poprzez dodanie kolejnych metod.

Jak byś widział natomiast taką rzecz. Część meczy ma relacje, część nie. Te które mają relacje, mogłyby pobierać swój wynik z relacji(na podstawie zdarzeń). Jakie podejście było by lepsze, osobne klasy Mecz i MeczLive czy klasa Mecz, która by w metodzie wynik() sprawdzała czy istnieje relacja i jeżeli istnieje to pobierała ją ?
Jarosław Żeliński

Jarosław Żeliński Analityk i
Projektant Systemów

Temat: Przykład DDD

Wojciech Soczyński:
Jak byś widział natomiast taką rzecz. Część meczy ma relacje, część nie. Te które mają relacje, mogłyby pobierać swój wynik z relacji(na podstawie zdarzeń). Jakie podejście było by lepsze, osobne klasy Mecz i MeczLive czy klasa Mecz, która by w metodzie wynik() sprawdzała czy istnieje relacja i jeżeli istnieje to pobierała ją ?

Generalnie staram się trzymać zasady jednomiejscowego skupienia odpowiedzialności, tu jedna klasa wie wszystko o meczach i ewentualnie ma specjalizowanych "pomocników" ... możliwe, że komponent z interfejsem IMecze.

Staram się minimalizować liczbę relacji (to w końcu atrybuty) na korzyść identyfikacji poprzez poprzez zawartość atrybutu (np. pomiędzy klientem i umową jaka zawarł nie ma relacji, umowa zawiera np. KRS klienta i po tym łatwo go znajdę w kontenerze z klientami. Nie trzeba wtedy zabezpieczać się przed pociągnięciem z repozytorium zbyt wielu danych.Jarek Żeliński edytował(a) ten post dnia 15.11.10 o godzinie 18:39
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Przykład DDD

Jarek Żeliński:
Wojciech Soczyński:
Jak byś widział natomiast taką rzecz. Część meczy ma relacje, część nie. Te które mają relacje, mogłyby pobierać swój wynik z relacji(na podstawie zdarzeń). Jakie podejście było by lepsze, osobne klasy Mecz i MeczLive czy klasa Mecz, która by w metodzie wynik() sprawdzała czy istnieje relacja i jeżeli istnieje to pobierała ją ?

Generalnie staram się trzymać zasady jednomiejscowego skupienia odpowiedzialności, tu jedna klasa wie wszystko o meczach i ewentualnie ma specjalizowanych "pomocników" ... możliwe, że komponent z interfejsem IMecze.

Staram się minimalizować liczbę relacji (to w końcu atrybuty) na korzyść identyfikacji poprzez poprzez zawartość atrybutu (np. pomiędzy klientem i umową jaka zawarł nie ma relacji, umowa zawiera np. KRS klienta i po tym łatwo go znajdę w kontenerze z klientami. Nie trzeba wtedy zabezpieczać się przed pociągnięciem z repozytorium zbyt wielu danych.Jarek Żeliński edytował(a) ten post dnia 15.11.10 o godzinie 18:39

Rozumiem, w takim razie może spróbuje zrobić jakiś diagram klas na podstawie tego co już się dowiedziałem i myślę, że będzie pole do dalszej dyskusji.
Jarosław Żeliński

Jarosław Żeliński Analityk i
Projektant Systemów

Temat: Przykład DDD

Wojciech Soczyński:
Rozumiem, w takim razie może spróbuje zrobić jakiś diagram klas na podstawie tego co już się dowiedziałem i myślę, że będzie pole do dalszej dyskusji.

do cudzych projektów zawsze należy mieć dystans, to co pisze to coś co mi "w duszy gra" :), ale poważnie tak myślę, nie raz pozornie skomplikowane algorytmy rozkładam na kilka obiektów i całość łatwo kleję w jeden wynik klasa sterującą.
Adam Brodziak

Adam Brodziak PHP, football, fun

Temat: Przykład DDD

Jarek Żeliński:
Generalnie staram się trzymać zasady jednomiejscowego skupienia odpowiedzialności, tu jedna klasa wie wszystko o meczach i ewentualnie ma specjalizowanych "pomocników" ... możliwe, że komponent z interfejsem IMecze.
Zgadzam się, że mecz powinien wiedzieć wszystko o sobie. Zauważ że relacja pomiędzy liczbą goli strzelonych a wynikiem nie jest zawsze zachowana, jak np. w przypadku walkowera 3:0

Staram się minimalizować liczbę relacji (to w końcu atrybuty) na korzyść identyfikacji poprzez poprzez zawartość atrybutu (np. pomiędzy klientem i umową jaka zawarł nie ma relacji, umowa zawiera np. KRS klienta i po tym łatwo go znajdę w kontenerze z klientami. Nie trzeba wtedy zabezpieczać się przed pociągnięciem z repozytorium zbyt wielu danych.Jarek Żeliński edytował(a) ten post dnia 15.11.10 o godzinie 18:39
To mnie zaciekawiło muszę przynać. Co prawda tutaj mowa o czymś co jest naturalną częścią modelu klienta, ale czy to samo tyczy się referencji do sztucznego ID klienta? To znaczy rozumiem że jesteś zwolennikiem leniwego ładowania obiektów z repozytorium, ale czy realne i sztuczne atrybuty mają taką samą wartość i znaczenie? Zwłaszcza w kontekscie ORM.
Jarosław Żeliński

Jarosław Żeliński Analityk i
Projektant Systemów

Temat: Przykład DDD

Adam Brodziak:
Staram się minimalizować liczbę relacji (to w końcu atrybuty) na korzyść identyfikacji poprzez poprzez zawartość atrybutu (np. pomiędzy klientem i umową jaka zawarł nie ma relacji, umowa zawiera np. KRS klienta i po tym łatwo go znajdę w kontenerze z klientami. Nie trzeba wtedy zabezpieczać się przed pociągnięciem z repozytorium zbyt wielu danych.Jarek Żeliński edytował(a) ten post dnia 15.11.10 o godzinie 18:39
To mnie zaciekawiło muszę przynać. Co prawda tutaj mowa o czymś co jest naturalną częścią modelu klienta, ale czy to samo tyczy się referencji do sztucznego ID klienta?

sztuczne ID nie jest atrybutem obiektu. Powoływanie się zaś na jawny atrybut jest moim zdaniem naturalniejsze i łatwiejsze do zrozumienia.
To znaczy rozumiem że jesteś zwolennikiem leniwego ładowania obiektów z repozytorium, ale czy realne i sztuczne atrybuty mają taką samą wartość i znaczenie? Zwłaszcza w kontekscie ORM.

Wzorzec "leniwego ładowania obiektów" to właśnie lekarstwo na dużą (zbyt dużą??) liczbę asocjacji. Jawne powoływanie się na atrybuty pozwala uniknąć (ograniczyć) stosowanie tego wzorca. Osobiście stosuje metaforę: asocjacja to spinasz łączący dokumenty, powołanie się na atrybut to odnalezienie dokumentu w szafie na bazie jego sygnatury.

ORM to mega zadanie jeśli mapujemy klasy na relacyjny model danych (jeżeli koniecznie musi być to baza relacyjna), zastosowanie wzorców np. active records (lub warianty mapowania klasa->tablica) pozwala uprościć model danych kosztem tego, że sama baza danych bez aplikacji jest "bezwartościowa" ale chyba nie ma obowiązku trzymania logiki danych w bazie, "zna ją" aplikacja :)
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Przykład DDD

Wrzucam diagram o którym mówiłem. Pewnie będzie źle (raz tylko robiliśmy coś na studiach i było to dawno). Także sugestie co do poprawek mile widziane.

Obrazek
Jarosław Żeliński

Jarosław Żeliński Analityk i
Projektant Systemów

Temat: Przykład DDD

w zasadzie wygląda nieźle ale jedna uwaga, za M.Fowlerem: wiem doznają kompozycje ale nie wiem co to agregacja :) (polecam jego UML w kropelce).

Ja bym jednak wywalił to dziedziczenie i zostawił Zdarzenie z atrybutem oznaczającym rodzaj zdarzenia.



Wyślij zaproszenie do