Aplikacja do wypożyczania filmów w DDD

0

Cześć,
Wiem, że temat na forum przewijał się zapewne miliony razy i sam na kilka wątków trafiłem ale dalej mam sporo wątpliwości i może na moim przykładzie łatwiej będzie mi to zrozumieć.
Chce napisać sobie małą apke "wypożyczalnia video" (zainspirowany ). Chciałbym ze to chociaż troche przypominało ddd, plus dookoła jakiś heksagonal. Domyślam się, że to wszystko to jest/będzie zapewne 'lekki' overengineering, ale na małym projekcie prościej będzie mi to zrozumieć i poukładać sobie w głowie.
Zacząłem sobie od domeny no i mamy Film. Robie sobie obiekt domenowy, który ma:

@Value
 class Film {
    Director director;
    Title title;
    Price price;
    Studio studio;
    Genre genre;
    Language language;
    Country country;

No tak. Normalnie kusiłoby mnie, żeby title dac jako String, cene jak BigDecimal albo w ogole double itp itd. I tak jak to, ze cene fajnie sobie przedstawić jako valueObject to mnie jeszcze kupuje, to np mam problem z takimi bytami jak, director, studio(...). To, że reżysera trzymam jako VO 'dziwnie' mi w kodzie wygląda.

  1. na razie mnie nie interesuje gdzie to bede trzymal (sql/nosql/hashmapa) i zakladam, ze to dobrze bo domena jest odseparowana od persystencji. Nawet nie wiem czy bede chcial tworzyc tabele/kolekcje jakąkolwiek osobna strukture 'bazodanowa' pod np. reżysera. Może do końca będzie to tylko sucha informacja przypisana do filmu.
  2. Ale... może jednak okaże się, że chciałbym wyciagnac z 'bazy' wszystkie filmy danego rezysera. W takiem sytuacji np w jakiejsc relacyjnej bazie juz wleci tabela director, i wtedy tez w moim kodzie rezyser jako taki nie bedzie juz prostym VO bo bedzie musial miec chocby id (choc czy domain object potrzebuje, powinien miec id? Domnowo film moze sie skladac, z tytulu, rezysera, ceny itd. ale id bazodanowy to srednio mi w 'jestestwo' filmu pasuje).
  3. A skoro okazaloby sie, ze jedanak Director bedze czyms wiecej niz VO, to moze Film powienien miec tylko jego ID a nie calego rezysera?
    Wiem, że to co napisałem jest dosc chaotyczne, ale sprowadza sie do - jak powinienem zapatrywac sie na VO i jak nawiazywac relacje miedzy domain object Film a potencialnym Domain Object Director?
0

W DDD masz coś takiego jak Bounded Context. I w jednym BC Director może być value objectem, a w drugim encją (entity czy nawet agregatem). Twój problem wynika z tego, że próbujesz zamodelować złożoną architekturę na prostym przykładzie oraz próbujesz mieć jeden model do wszystkiego: zapisy, odczyty, wyszukiwania, raporty - bład. Jeżeli chcesz mieć jeden model do wszystkiego do użyj normalnej bazy relacyjnej. Jeżeli chcesz mieć kilka modeli, każdy dostosowany do konkretnego przypadku użycia czytaj dalej.

To co piszesz o pobieraniu filmów danego reżysera to jest bardziej związane z poddomeną wyszukiwanie niż z poddomeną Wypożyczanie. Dlatego też między innymi powstały mikroserwisy, które często dzieli się właśnie po Bounded Contextach. Czyli jeden mikroserwis/BC - Wypożyczanie ma np. bazę relacyjną, a drugi mikroserwis/BC Wyszukiwanie nierelacyjną zoptymalizowaną pod wyszukiwanie taką jak Cassandra. Oczywiście dane są asynchronicznie replikowane pomiędzy mikroserwisami i siedzą w osobnych bazach danych co prowadzi do ich duplikacji, ale to jest konsekwencja naszego wyboru i kompromis na który się godzimy chcąc mieć możliwość wygodnej obsługi wypożyczeń oraz wyszukiwania informacji.

W tak prostym przypadku jak twój zamiast bawić się w jakieś peristance ignorance, podepnij sobie zwykłą bazę relacyjną jak postgres i gotowe.

Możesz też podzielić aplikację na osobne moduły i każdy moduł wtedy ma dedykowany data store i model domenowy, ale aplikacja wdrażana jest jako całość, ale generalnie wszystko sprowadza się do tego, żeby każdy moduł/bounded context miał osobny model domenowy

0

Jest jeszcze coś takiego jak Stringly typed development (zamiast strongly typed) - wszędzie dookoła same Stringi i łatwo się pomylić. Aby się przed tym uchronić, warto wrapować typy proste w takie właśnie data classy. To nie muszą być od razu obiekty domenowe, które w takiej postaci będziesz trzymał w repozytorium. Nie trzeba używać DDD, żeby to stosować.

0
roland27 napisał(a):

Zacząłem sobie od domeny no i mamy Film. Robie sobie obiekt domenowy, który ma:

@Value
 class Film {
    Director director;
    Title title;
    Price price;
    Studio studio;
    Genre genre;
    Language language;
    Country country;

Dziwne że zaczynasz od klasy Film z takimi wartościami. Nie wydaje mi się żeby to był najlepszy pomysł na start.

0

Dziękuję za odpowiedzi! Pozwole sobie w jednym komentarzu się odnieść;]
@markone_dev:
"Twój problem wynika z tego, że próbujesz zamodelować złożoną architekturę " - to prawda, ale wolałem pojść w cość prostego i mocno "rzeczywistego", żeby nie zakopać sie w zbytniej abstrakcji tylko zlapac sam koncept.
"próbujesz mieć jeden model do wszystkiego: zapisy, odczyty, wyszukiwania, raporty - bład.(...)" oraz "To co piszesz o pobieraniu filmów danego reżysera to jest bardziej związane z poddomeną wyszukiwanie niż z poddomeną Wypożyczanie.(...)" - Brzmi sensownie i intuicyjnie, zapewne to jak ja to opisalem wynika z mojego sredniego na razie rozumienia ddd. Mój zamysl wzial sie z podejscia zrozumienia, ze mam juz domene Film, a film moge: wypozyczyc(zapisc), moge wziac i przeczytac o nim info(wyszukiwanie), zwrocic, itp. itd. Ogolnie, zalozylem ze skoro mam domene film i encje domenowa to ona mi to ma to opcje umozliwic(w niej ma sie dziac logika biznesowa jak wypozeczenie filmu, zobaczenie jego 'detali' [troche jak w faktycznej wypożyczalni wziecie filmu do reki i przeczytanie okladki], ale moze zle rozumiem tutaj logike biznesowa).
"To co piszesz o pobieraniu filmów danego reżysera to jest bardziej związane z poddomeną wyszukiwanie niż z poddomeną Wypożyczanie"
"Możesz też podzielić aplikację na osobne moduły i każdy moduł wtedy ma dedykowany data store i model domenowy, ale aplikacja wdrażana jest jako całość, ale generalnie wszystko sprowadza się do tego, żeby każdy moduł/bounded context miał osobny model domenowy" - jezeli dobrze rozumiem, jeden modul, powiedzmy wyszukiwanie moze dzialac na bogatnym Filmie, bo chce o nim duzo informacji. W domenie wypozyczanie dzialalbym/bede dzialal na przykladowo obiekcie domenowy Rent/Order nazwa na razie jest drugorzędna i tam bede wsadzal Film, ale nie w rozumieniu Film - obiekt domenowy z podmodulu wyszukiwanie tylko jakis DTO przykladowo rentFilm? Czy w miare dobrze to rozumiem?;]

@Charles_Ray Przyznam sie ze nie slyszlalem stringly/strongly typed, dzieki! Wyglada to dla mnie czytelniej faktycznie jedyny problem jaki z tym na razie mam no natłok klas ktore sa dosc "biednymi" strukturami danych.
@Riddle To znaczy?;] za biedna/bogata czy w ogole od tylka strony?;]

0

to prawda, ale wolałem pojść w cość prostego i mocno "rzeczywistego", żeby nie zakopać sie w zbytniej abstrakcji tylko zlapac sam koncept.

Coś prostego kłóci z się z ideą Domain Driven Design. Co do rzeczywistości twoich use case'ów to może i są rzeczywiste, ale ich logika jest banalna. Przynajmniej ja nie widzę tam złożonej logiki, która nadaje się do modelowania i wyrażania za pomocą taktycznego DDD.

Mój zamysl wzial sie z podejscia zrozumienia, ze mam juz domene Film, a film moge: wypozyczyc(zapisc), moge wziac i przeczytac o nim info(wyszukiwanie), zwrocic, itp. itd. Ogolnie, zalozylem ze skoro mam domene film i encje domenowa to ona mi to ma to opcje umozliwic(w niej ma sie dziac logika biznesowa jak wypozeczenie filmu, zobaczenie jego 'detali' [troche jak w faktycznej wypożyczalni wziecie filmu do reki i przeczytanie okladki], ale moze zle rozumiem tutaj logike biznesowa).

Film to nie dziedzina. Dziedziną biznesową (domain) jest Wypożyczalnia filmów. Wszystko co się z tym wiąże płatności, zwroty, wyszukiwania to ewentualnie mogą być poddziedziny (subdomains). Ewentualnie bo to zależy. Poddziedziny dzielimy na głowne (core subdomains), wspierające (supporting subdomains) i generyczne (generic subdomains). W zależności od typu poddziedziny stosujemy inne wzorce, a w przypadku poddziedzin generycznych kupujemy i integrujemy z aplikacją gotowce. Przykładowo, mało kto pisze swój system do księgowania i fakturowania. Zwykle kupujemy rozwiązanie, które dostarcza API i integrujemy się z nim właśnie w ten sposób. Potem jak chcemy wystawić fakturę to wysyłamy request do API systemu księgowego i cała podatkowa magia dzieje się tam.

Idealnie jeśli jedna poddziedzina == jeden bounded context, co oznacza że każda poddziedzina ma swój własny model biznesowy. Ale rzeczywistość jest brutalna i zwykle tak nie jest. Poczytaj o mapowaniu i mapach kontekstów (Context Mapping/Maps) oraz technice Event Storming.

jezeli dobrze rozumiem, jeden modul, powiedzmy wyszukiwanie moze dzialac na bogatnym Filmie, bo chce o nim duzo informacji. W domenie wypozyczanie dzialalbym/bede dzialal na przykladowo obiekcie domenowy Rent/Order nazwa na razie jest drugorzędna i tam bede wsadzal Film, ale nie w rozumieniu Film - obiekt domenowy z podmodulu wyszukiwanie tylko jakis DTO przykladowo rentFilm? Czy w miare dobrze to rozumiem?;]

Mniej więcej dobrze. Chodzi o to, że pewne poddziedziny są grube i wymagają modelowania znanego z taktycznego DDD jak agregaty, encje, obiekty wartości, polityki, repozytoria, fabryki, buildery, a inne poddziedziny są cienkie (jak wyszukiwarki), gdzie zwykle obiekty są czymś w rodzaju anemicznych encji, czyli obiektami w stylu DTO/ViewModel, które nie posiadają logiki tylko same atrybuty i są dostosowane pod wyszukiwanie/wyświetlanie na ekranie.

EDIT:

Tu ważna uwaga, że to czy poddziedzina jest główną, wspierającą czy generyczną zależy od modelu biznesowego przedsiębiorstwa. Przykładowo w takiej wypożyczalni jak twoja to poddziedzina Wyszukiwanie może być główną, ponieważ to w jaki sposób wyniki wyszukiwania są dopasowane do potencjalnego klienta świadczy o przewadze firmy nad pozostałymi, a proces zamawiania czy wypożyczania filmu to będzie poddziedzina wspierająca. Podobnie mogłoby być z poddziedziną Reklamacje jeżeli sposób obsługi reklamacji stanowi o innowacyjności i przewadze konkurencyjnej przedsiębiorstwa to właśnie ta dziedzina będzie główną.

No i na koniec to, że jakaś poddziedzina jest główną, nie oznacza, że z automatu nadaje się do stosowania taktycznego DDD i modelowania wszystkiego za pomocą taktycznych building blocków jak agregaty. Może się okazać, że do wyrażenie dziedziny głównej będziemy chcieli użyć innych wzorców ze względu na jej specyfikę - na przykład elementy Machine Learning czy jakiejś analizy danych.

0
@Value
 class Film {
    Director director;
    Title title;
    Price price;
    Studio studio;
    Genre genre;
    Language language;
    Country country;

price to nie jest cecha filmu. Ten sam film możesz chcieć wypożyczyć jako VOD, na płycie DVD i na kasecie video i w każdym tym przypadku wypożyczać to za inną cenę (albo książki - miękka okładka, twarda okładka, e-book - 3 różne produkty, a jedna książka).

genre, language, country, director - filmy mogą mieć te właściwości w liczbie mnogiej. Wielogatunkowy film, na którym mówią w kilku językach, stworzony przez kilku reżyserów w różnych krajach. I co zrobisz?

Przy czym nie chodzi mi tutaj o argumentację, jakie klasy masz mieć, tylko zwracam uwagę na to, że pierwotne założenia mogą się zmienić. Może zamiast próbować zaprojektować wszystko idealnie, to lepiej zaakceptuj to, że kod będzie się zmieniać. Dobrym ćwiczeniem będzie jak zaczniesz pisać wg jakichś tam pierwotnych założeń, ale w pewnym momencie zaczniesz przerabiać kod, żeby spełnił nowe założenia.

To, że reżysera trzymam jako VO 'dziwnie' mi w kodzie wygląda.

Też tego nie rozumiem. Reżyser to konkretna osoba("encja"), a nie sam string z imieniem i nazwiskiem (Ani imię i nazwisko nie identyfikuje jednoznacznie reżysera ani nie jest stałe i może się zmienić z powodu ślubu, kaprysu czy zmiany płci).

1 użytkowników online, w tym zalogowanych: 0, gości: 1