Non-anemic entities

0

Cześć, ostatnio gdzieś tu na forum znalazłem sprzeczną z moim ostatniem podejściem, które stosujemy w pracy problematyką. Chodzi o NIEANEMICZNE encje. W pracy spotkałem się po raz pierwszy z tym i architekci wprowadzili taką zasadę u nas- podobno źródłem tego jest książka o DDD - aby encje hibernatowe posiadały również metody biznesowe.

Tutaj jednak czytałem, że to jest złe. Mi się z tym dobrze pracuje.
Ktoś mógłby ruszyć bardziej ten problem ?

9

Mam nadzieje że żartujesz teraz :D A jeśli nie to zalecam szybką ewakuacje w tejże firmy bo macie architektów idiotów.
Encje w rozumieniu DDD nie mają NIC WSPÓLNEGO z klasami @Entity mapującymi tabele w bazie danych. @jarekr000000 się na pewno ucieszy że ktoś tak mocno nie zrozumiał DDD że zaczyna promować jego ulubioną metodykę "encja na twarz i pchasz".
Jesteś pewien że ty czegoś źle nie zrozumiałeś czasami? :)

0

"encja na twarz i pchasz".

title

2

To fakt, DDD promuje nieanemiczne encje, ale to nic nowego, bo to po prostu programowanie obiektowe. Jeżeli się nie operuje na strukturach danych, a obiektach to te drugie powinny się charakteryzować jakimś zachowaniem. Jeżeli masz struktury danych, które w Javie są klasami jak wszystko inne, ale każda operacja dzieję się tylko za pomocą klas 'serwisów', które robią głównie getX, setY to cięzko mówić o obiektowości. Encje w DDD tak jak napisał Shalom nie mają nic wspólnego z encjami w ORMach i jeżeli trzymać się sztywno założeń DDD to są złamaniem reguł, bo wiążesz kod biznesowy z warstwą infrastruktury, ale widziałem różne podejścia i generalnie może mieć to sens, by encje hibernatowe również posiadały jakieś zachowanie i były czymś więcej niż struktury danych, z czasem zespół pójdze po rozum do głowy i stwierdzi, że jednak lepiej zrobić to po bożemu

0

Ale to chyba nie jest pchanie encji na twarz ? Przecież do kontrolerów zwracam już skonwertowane dtosy. Tutaj np encję hibernatową: Event, która ma np. taką metodę biznesową jak finish(), która tam sprawdza jakieś warunki i coś robi np. z polem date. Potem w serwisie robię eventsRepository.save(event);

Co w tym jest złego i jak to inaczej robić ?

0

Pół biedy - tak bym to określił, bo z tego co rozumiem to jedynym narzutem kody infrastrukturalnego na encję są adnotacje hibernatowe. Nie jest to takie złe, ale w idealnym świecie miałbys encję w rozumieniu DDD bez adnotacji i implementacja repozytorium zawierałaby klasę, która dopiero byłaby wykorzystywana przez ORM czy tam cokolwiek innego. Generalnei często zdarza się (prędzej lub później zawsze?), że jeżeli klasa zaadnotowana jako Entity czy z czego tam kto korzysta przecieka i jest pisana pod katem tego, żeby być mapowalną na tabele, a to już jest szczegół, który nie jest wymagany do poprawnego działania biznesu/domeny. Dlatego idealnym rozwiązaniem jest całkowite rozdzielenie kodu biznesowego od wszystkich zależności

1

@Bambo: widze że oglądałeś tylko pół talka na temat "encja na twarz" ;) Wystartuj teraz tutaj https://youtu.be/SxqK8jo7vdY?t=1795
Generalnie to jest zły pomysł zeby sobie tak mocno związać model danych z logiką biznesową, bo zwyczajnie ograniczasz sobie możliwości zmiany czegokolwiek i dodatkowo na 99% zacznie to wyciekać. Ty sobie niby zrobisz jakieś metody biznesowe, ale obok nich będzie "get" i "set" i problem gotowy.

0

explain like im 5 not even junior

dlaczego get = zło?

0

Sam w sobie get to nie zło, jeśli siedzi w jakimś DTO/strukturze danych - siłą rzeczy musi tam coś takiego być.
Ale get w obiekcie domenowym zwykle oznacza że łamiesz enkapsulacje, bo zamiast udostępniać jakieś "funkcjonalności" to ty po prostu po chamsku wystawiasz przez publiczne API stan twojego obiektu. Potem próba zmiany wewnętrznej struktury takiego obiektu jest praktycznie niemożliwa bez zmiany w połowie codebase.

1

Jak już miałbym wybierać między dwoma zrypanymi architekturami to chyba bardziej mnie wkurza wersja z torebką na głowie. Każde entity hibernatowe ma 1 do 1 odpowiednik w DTO. Udajemy, że jest jakaś separacja. Z drugiej strony dzięki powszechnemu niezrozumieniu działania JPA/Hibernate architektura taka gwarantuje dobrą zabawę: zmiany, które się zapisują do bazy oraz zapisy oraz nulle w bazie, które nie wiadomo z czego się biorą. Najlepsi potrafią nawet generatory kodu do tego mapowania DTO <-> hibernate napisać. Miodzio.

Wersja bez DTO, wydzielonej logiki , gdzie wszystko jest w encjach to przynajmniej jasne postawienie sprawy. Bez udawania. Takie architektoniczne: biore 150 za godzinę, nie musisz mi kupować drinka.

A co do: architekci w mojej firme przeczytali książke, pojechali na konrefencję.... to można by ładny poemat napisać. Jak ktoś całą głowę ma prostokątną, bo 5 lat tłukł CRUDy do tabelek, to łatwo z tego nie wyjdzie.
Zresztą. Poniżej przesławny skecz - w polskiej wersji językowej.

0

To w takim razie koledzy ... macie może gdzieś jakiś przykład albo moglibyście sami podać, w którym jest jakiś prosty kontroler, serwis, encja JPA i encja domenowa ? Bo szczerze mówiąc też mi się nie podoba pchanie logiki do serwisu, która ustawia coś na obiekcie, a szczerze mówiąc nigdy się nie spotkałem z taką architekturą, gdzie te encje domenowe są wydzielone od JPA.

Nie wyobrażam sobie nie mieć getterów w encjach JPA - jak wtedy konwertujemy encje JPA na jakieś dtosy, które chcemy zwrócić ?

Zdr :)

0

tez chetnie bym zobaczyl jakies repo gdzie to jest zastosowane :)

0

Ja tez.

0

inna sprawa, ze JPA po prostu jest mało into DDD :D

3
filemonczyk napisał(a):

tez chetnie bym zobaczyl jakies repo gdzie to jest zastosowane :)

Off - top. MSPANC.

Programista korpo-javowy pojechał dla odprężenia na ryby i, jak to zwykle w takich przypadkach bywa, złapał złotą rybkę.

  • Wypuść mnie do wody, a spełnię twoje życzenie! - błagalnie krzyknęła złota rybka.
  • Jak to życzenie? Jedno ?! Normalnie są trzy! - dopytał zaskoczony, ale czujny programista.
  • Bo ja jestem rybka Enterprise Edition i obsługuję tylko jedno życzenie na złowienie - wyjaśniła - poza tym, proszę dawaj życzenie szybko, bo się duszę na powietrzu.
  • Ok.... w takim razie wiesz co.... chcę super potężnego laptopa. Ma mieć moc wszystkich chińskich serwerów, ma ważyć pół kilo, bateria ma trzymać 10 godzin.. no i ma na tym działać arch linux....
  • Stop - przerwała rybka - Troszkę przesada z tym laptopem. Nawet jak Ci takiego zrobię, to Cię złapie FBI i zapyta skąd masz. Będzie chryja.... wymyśl coś innego lepiej.
  • Dobra!... to może weź mi daj taki przykładowy dobrze zrobiony projekt w DDD z użyciem JPA i Springa. Żeby był czysty kod, i dobre testy, i w ogóle...
  • W jakim kolorze ma być ten lapek? - przerwała ponownie rybka.
4

DDD dla mnie to przerost buzzwordów nad treścią.
Do sukcesu potrzeba tylko znajomości: Javy, programowania obiektowego, zasad SOLID, wzorców projektowych (nie wszystkich) i znajomości heksagonalnej architektury. Brzmi prosto, ale takie nie jest :P

Jeżeli masz skomplikowany problem biznesowy:

  1. Tworzysz moduł w projekcie, gdzie modelujesz problem za pomocą niemutowalnych klas Javy, strategi, fasad i kompozycji zamiast dziedziczenia. W tym projekcie gdy potrzebujesz zrobić coś technicznego to robisz interfejs (np. *Repository, *Client) i nic więcej. Zależności nie ma - chyba, że jakieś utile jak Vavr, Lombock, Slf4j lub wyjątkowo cięższe jak Apache POI.
  2. Tworzysz drugi moduł, gdzie importujesz pierwszy moduł i implementujesz wszystkie interfejsy. Dodatkowo kleisz to w jeden system ręcznie lub za pomocą Spring DI / Guice.
  3. Odkrywasz, że JPA sprawia same problemy i niemal nie da się go użyć. :D Stosujesz JDBC lub JOOQ do implementowania Repozytoriów.
    Porada: jeżeli już jesteśmy zmuszeni użyć jakiegoś Spring Controllera, który potrzebuje mutowalny obiekt, możemy mu dać builder od Lombocka.

Jeżeli masz problem typu CRUD:

  1. Robisz jeden moduł ze Spring Data i Encjami. Gotowe.

SQL to świetny język dla skomplikowanych przypadków. I wolę analizować 10 zapytań biznesowych SQL niż 5 encji hibernate.
Nie wiem czemu ludzie lubią takie magie jak Spring i Hibernate w 2018 roku. Te frameworki włażą butami w wasze klasy, modyfikują stan, rzucają wyjątki itp. Same problemy.

0

@nie100sowny:
Zgodzę się z tym ;) dodatkowo wprowadza kilka rzeczy, które też często są oczywiste jak ubiquitous language. IMHO warto się z tym zapoznać

0

@nie100sowny:

Masz przykład dobrze zaprojektowanego takiego modułu ze skomplikowanym problemem ?
Nadal nie mogę znaleźć tego miejsca, w którym JPA tworzy problem. Albo inaczej .. sam się kiedyś zastanawiałem, że w tym JPA/Hibernata to w sumie bardzo dużo adnotacji i magii i trzeba tego używać, aby było optymalniej, a przecież walnąć jakieś natywne SQLe w JDBC to przecież nie jest problem a mamy tak dużo wtedy kontroli nad tym wszystkim. Chyba, że jest jeszcze inny powód dla którego skutecznie się na tym forum beszta to JPA ?

A co do typowego CRUDA, napisałeś, że spring data i encje, ale co ? Przecież jakaś tam logika w serwisach jest - nie masz żadnych dto ?

Cały czas staram się dociec jak wygląda flow od kontrolera do konkretnej zmiany w tabelce bazy danych i odwrotnie. No bo wychodzi na to, że:

  1. Jak jest encja jpa nieanemiczna i zamiast setterów udostępnia konkretne biznesowe metody + ma gettery aby zbudować jakieś dto'sy do wypychania na zewnątrz to źle
  2. Jak encja jpa jest czystą strukturą i jej logika znajduje się w serwisie to też źle.

Jak żyć :D Ale dzięki za odpowiedzi :D

0

czy wg Martina Fowlera (https://www.martinfowler.com/bliki/AnemicDomainModel.html) **nie **pisanie funkcji biznesowych w klasach z adnotacją @Entity - jest anty-wzorcem? bo chyba zle to rozumiem

1

U nas doszliśmy do wniosku, że lepszy dobry transaction script niż słabe DDD.
DDD jest trudne zwłaszcza kiedy projekt tworzymy od początku, gdzie ciężko jest wyznaczyć Aggregate Root'y (BoundedContext), z drugiej strony kogo stać aby 5 letni projekt wykonany w transaction script przepisać na DDD. (no chyba Allegro, Amazona i Netflix'a)...później się tych chwalą i my też tak chcemy :-p

Aby zrobić DDD trzeba mieć mocny zespół(albo robić dużo code-review, spotkań projektowych) jednak na rynku jest przewaga średniaków, dla których zrozumienie controller->service->repo->entity jest prostsze/możliwe.....

Co do przykładu to można zobaczyć prekursora DDD w Polsce w akcji na https://github.com/BottegaIT/ddd-leaven-v2

0

@nie100sowny:
A w przypadku prostego projektu, gdzie nie ma dużo logiki to jak robisz ?
Bo ja się cały czas zastanawiam, czy to całe DDD z tymi CQRSami nie powinno być stosowane do wielkich projektów, a jeśli ja mam 5-6 modułów i raczej średnie wielkości aplikacje to czy to w ogóle jest sens tego używać.

I jeszcze jedno mnie zastanawia. Które klasy u nas powinny być immutable ? Tylko Value Object Class, które są opakowaniem np adresów email, zip codów, nr tel itp. ?

Własnie na dniach będę zaczynał pisać raczej małej wielkości resta do aplikacji mobilnej. Chciałbym w niej zastosować jakąś lepszą technologię. Do tej pory moja kariera springowa przebiegała tak:

  1. Encja na twarz bez żadnych dtosów
  2. Oddzielenie encji jpa od dto, ale cała logika była w serwisach, więc był to raczej kod proceduralny
  3. Encje jpa z logiką biznesową, odchudzone serwisy, dtosy z konwerterami.
  4. ?
1

A ja mam jeszcze inne doświadczenie z podejściem DDD i używaniem w nim JPA. Stary dobry CQS. Polecenia, czyli C przyjmują jako parametry wejściowe encje JPA, albo ich fragmenty. Dzięki temu operacje CUD są stosunkowo proste i nie trzeba się bujać z dziwnymi translacjami. Zapytania, czyli Q, reprezentują obiekty domenowe zatem z JPA wychodzi projekcja, która po lekkim "ospringowaniu", może być używana dalej w serwisach. Zero magii.

0
Bambo napisał(a):
  1. Oddzielenie encji jpa od dto, ale cała logika była w serwisach, więc był to raczej kod proceduralny

To trochę zależy jak popatrzymy na programowanie obiektowe, jeżeli jak na zasoby posiadające stan ze zbiorem funkcji stanowiących ich interfejs, to takie anemiczne encje z interfejsem w postaci serwisów też można traktować jako OOP :p Zbiór końcówek http w sumie też. Zasobem może być encja, ale może to też być rekord w bazie danych albo plik w systemie plików a jego interfejsem zbiór exeków. Trzeba się zdystansować :)
Wrzucanie logiki do encji raczej nie wyjdzie, bo jak jedna encja będzie chciała wywołać metodę drugiej to i tak powinna to zrobić poprzez serwis.
Pisanie DTO to dla mnie strata czasu, odłączona encja też jest DTO tylko trzeba pilnować granic transakcji. Już samo pisanie encji mnie denerwuje i czasem zastanawiam się czy nie byłoby lepiej przerzucić logikę do proc. składowanych :p

3
student pro napisał(a):

Pisanie DTO to dla mnie strata czasu, odłączona encja też jest DTO tylko trzeba pilnować granic transakcji[...]

Jesli jakiś framework, język, paradygmat wymaga abyś czegoś pilnował, był uważny to to jest właśnie recepta na katastrofę.
Człowiek może być uważny raz na jakiś czas - ale zespół napierniczający kod 5 dni w tygodniu po osiem godzin - nie będzie stale czujny.

Błedy związane z podpiętymi (lub nie ) do sesji encjami JPA są całkiem przykre w debugowaniu.

Mam już całkiem pokaźny zbiór historyjek związanych z tym problemem. Ktoś coś tam zmienił na użytek GUI, debugu...w podłączonej encji i pół bazy się skasowało...
albo podpiął w relacji parent - child obiekt pochodzący z zagnieżdzonej transakcji. Miodzio. Takie rzeczy potrafią całkiem zatrzymać development w projekcie na tydzień lub dwa.

Dla mnie ten aspekt JPA łamie po prostu zasady enkapsulacji.
DTOsy , jakkolwiek w większości projektów zrobione w sposób chory, przynajmniej trochę przed tym zabezpieczają. W JPA ````select new DTO() ``` to w zasadzie podstawa zdrowego trybu życia.

0
student pro napisał(a):
Bambo napisał(a):
  1. Oddzielenie encji jpa od dto, ale cała logika była w serwisach, więc był to raczej kod proceduralny

To trochę zależy jak popatrzymy na programowanie obiektowe, jeżeli jak na zasoby posiadające stan ze zbiorem funkcji stanowiących ich interfejs, to takie anemiczne encje z interfejsem w postaci serwisów też można traktować jako OOP :p

Jak dla mnie to kod strukturalny, definiujesz struktury, a później na nich operację, przez co np. to operujący na strukturze musi ją walidować albo sprawdzać reguły biznesowe.

0

https://github.com/jakubnabrdalik/hentai <-

Ale tutaj w ogóle nie ma typowych encji biznesowych. Mamy dtosy i konwerter dto -> encja jpa, na które ktoś wcześniej w tym temacie nakrzyczał, że są złe. A logika jest w serwisie (fasadzie).

0

Ok, po tym filmie Jakuba trochę lepiej to widzę i się z tym lepiej czuję. Ale jeszcze rzecz mnie naszła. Jak mamy encje JPA i załóżmy jest to jakaś encja Usera. To czy jeśli user ma jakiś adres, na który składa się miasto, osiedle, kod pocztowy itd, to czy warto ten adres też opakować w jakąś embeddabled encje czy skoro to jest i tak głupia struktura bez żadnych metod to zrobić tam po prostu stringi i inne prymitywy ? Bo wiadomo, obiekty domenowe to będzie user, adres, zipcode, city itd.

0

Tym czego szukasz jest w DDD Value Object, a w świecie poza DDD po prostu strukturą danych. Kilka prymitów, które połączone mają jakiś sens warto zebrać w strukturę/klasę. Łatwiej będzie refaktorować, z czasem może dojdzie jakieś zachowanie etc.

Dawno nic nie robiłem w JPA, ale z tego co rozumiem to obiekt domenowy jak najbardziej powinien zawierać obiekt zagnieżdzony np. adres (zamiast pól adresu jako prymitywy). To jak to zmapujesz to w bazie danych to jest naprawdę szczegół i powinno w dużej mierze zależeć od możliwosći bazy/ORMa. https://en.wikibooks.org/wiki/Java_Persistence/Embeddables - pierwszy przykład wydaje się spoko, nie ma tam żadnego IDka, po prostu umożliwia wbudowanie struktur w jedną tabelę co by kod javowy miał więcej sensu.

1

@Bambo ja generalnie jestem mocno przeciwny używaniem w aplikacji prymitywów i klas standardowych, bo mocno ogranicza nam to flexibility przy pisaniu kodu, szczególnie w miejscach gdzie jest spora szansa że coś będziemy zmieniać. Dodanie nowego pola, jak już mamy jakąśtam strukturę/klasę opakowującą, to jest prosta sprawa, ot dochodzi jedna linijka. A jak gdzieś sobie wpiszesz wartość jako Stringa czy Inta to potem już niewiele można zrobić, bo zmiana powoduje konieczność zmiany API które z tego korzystały. Więc trochę zdrowego rozsądku trzeba mieć.
W tym przypadku to choćby dla czytelności warto wyrzucić sobie te dane do osobnej struktury.

0

@Shalom:
@hcubyc
No ok czyli wychodzi na to, że będę miał lustrzane odbicie. Po stronie biznesu mam takie encje jak User, Adress, ZipCode, Email itd. - z metodami biznesowymi do nich, a po stronie bazy danych mam odpowiadające im struktury danych - encje JPA.

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