Най-добра практика Domain Model, DTO, Service

67 views
Skip to first unread message

Trifon Trifonov

unread,
Nov 1, 2020, 4:49:29 PM11/1/20
to Bulgarian Java Users Group
Здравейте, колеги,

проект с многослойна архитектура, както следва:
- моделен слой/Domain model, пример: Product.java
- хранилище(достъп до БД)/Repository, пример: ProductRepository.java (използва Product)
- бизнес логика/Service, пример: java interface ProductService.java и ProductServiceImpl.java (използва Product и ProductRepository)
- междиннен слой/DTO, пример: ProductDTO1.java, ProductDTO2.java
- REST услуги, пример: ProductResource.java (използва ProductService и ProductDTO)

Ако използваме DTO (Domain Transfer Object), аз предпочитам да използвам DTO, за да не връщам на клиентите директно Domain обекти, то тогава слоят с бизнес логиката трябва ли да работи и с DTO класовете?

Как практикувате?

Поздрави,
Трифон

Nikolay Vasilev

unread,
Nov 1, 2020, 5:19:15 PM11/1/20
to bg-...@googlegroups.com
Привет, Трифон,

Аз навремето правех отделни обекти за всеки слой и ги обръщах от един обект в друг, но ако ми е сравнително директна логиката напоследък ползвам домейн обектите за предаване на информацията.

Сериализирането от контролера към клиента може да е от домейн обекта към някакво външно DTO, за нуждите на апито и то само ако имаш нужда да променяш елементи от презентацията на домейн обекта.

Разбира се всичко зависи от случая, някои домейни могат да изискват трансформации между всеки слой - поддръжката на тези трансформации обаче е и скъпа и податлива на грешки ако се прави на ръка.

Поздрави,
Николай

--
Получихте това съобщение, защото сте абонирани за групата „Bulgarian Java Users Group“ в Google Групи.
За да се отпишете от тази група и да престанете да получавате имейли от нея, изпратете имейл до bg-jug+un...@googlegroups.com.
За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/4bfa1981-7dbc-4588-94eb-4c943e95c95bn%40googlegroups.com.

Victor Penelski

unread,
Nov 1, 2020, 6:19:02 PM11/1/20
to bg-...@googlegroups.com
Здравей Трифоне,

В един проект преди няколко години се постарах да следвам дословно "hexagonal architecture" (aka "onion", "clean", "ports and adapters"...), към което държах и отделна репрезентация на ресурсите си за всекси слой - пр. Product (domain), ProductEntity (repository), ProductRequestResource / Product ResponseResource (rest api), ProductDTO (external system), etc..

Проекта не беше голям и когато го погледнах в ретроспектива, всички тези прехвърлянки донесоха само излишен overhead.
Предполагам, че при по-голям проект със сложен домейн би имало значително повече смисъл.

Имаше обаче едно нещо, което много ми допадна - пазенето на чист domain layer.

Ако това се спазва от началото на проекта, няма никакъв проблем например "rest controller"-a да се приема / връща директно domain обект. Ако в бъдеще се наложи гъвкавост, винаги може да се добави междинна репрезентация, която да знае как да се преобразува до чистата domain репрезентация, без да "замърсява" нищо по пътя.

От там излиза "правилото" Domain-a да няма dependency-та към други слоеве, а да предоставя интерфейси (портове), към които други слоеве да имат dependency.. Към domain включвам както имплементация на бизнес логика, работеща с domain обекти, така и xxxRepository (което е де-факто порт), с уговорката, че това е само интерфейс и ако има нужда от конкретна имплементация, тя ще зависи на този интерфейс, но домейна няма да знае за нея.

> Ако използваме DTO (Domain Transfer Object), аз предпочитам да използвам DTO, за да не връщам на клиентите директно Domain обекти, то тогава слоят с бизнес логиката трябва ли да работи и с DTO класовете?
обратното - DTO знае как да се преобразува към Domain обект, а "бизнес логиката" не я интересува какви/колко DTO-та има.

няколко ресурса по темата, които ми допаднаха:
оригиналната статия за Ports and Adapters на https://alistair.cockburn.us/hexagonal-architecture/
малко старичка статия на Robert Martin - "The Clean Architecture" - https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
практична лекция на Victor Rentea, който я разказа и в София миналата година: Evolving a Pragmatic, Clean Architecture A Craftsman's Guide - https://www.youtube.com/watch?v=KOqIUNUq2Gg

Поздрави,
Виктор


За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/CA%2BAGwz70gfx%2Bk8zP%2Bk7GU8vzVQztm6a9GnEJ708jfY-VpLx3gw%40mail.gmail.com.

Nikolay Vasilev

unread,
Nov 1, 2020, 7:44:24 PM11/1/20
to bg-...@googlegroups.com
Тази също е много добра - 

За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/CAJ2whhVnXzF3-pd-42-B3H2ZzFxf-bHbOHDF4a%2BwPqSQjsisEA%40mail.gmail.com.

Daniel Dimov

unread,
Nov 2, 2020, 1:38:40 AM11/2/20
to Bulgarian Java Users Group
Здравей,

Според мен по-простата архитектура е по-добра. Тоест винаги когато може REST слоя да работи с домейн обектите. Ако е необходим обект с допълнителна/преработена информация - само тогава да се прави преобразуване от домейн обект(и) в DTO, като това преобразуване да става в слоя с бизнес логиката. REST слоя да се занимава само със специфичните неща около http комуникацията.

Даниел

Plamen Stefanov

unread,
Nov 2, 2020, 1:48:24 AM11/2/20
to Bulgarian Java Users Group
Здравей,

Както казаха колегите по-горе - зависи от проекта. Ако е нещо голямо, което ще се поддържа много време, ще има много версии и в последствие редовно ще се добавят/махат/променят функционалности - ползвай ДТО за всеки слой. Така изолираш презентациите в базата, рест и домейна, които могат да се променят според нуждите, които тепърва ще възникнат и не са известни на никого. От години работя при тези условия и съм видял, че ДТО за всеки слой е единствения вариант. Само си представи смяна на база (или ъпгрейд) или смяна на структурата й, поради перформънс проблеми.

Иначе за проект, в който изработваш нещо и предаваш на клиента - директния подход е най-добре, няма нужда от излишни усложнения.

Ако говорим за истински микро сервизи, тогава те са малки (микро) и можеш да ползваш директния подход. Тези сервизи по дефиниция никога не трябва да стават големи и сложни. Ако ги погледнеш всички заедно - те са части от различни домейни (или под-домейни на общия) и АПИ-тата им създават нужната изолация.

Поздрави
Пламен

Trifon Trifonov

unread,
Nov 2, 2020, 1:52:21 AM11/2/20
to Bulgarian Java Users Group
Благодаря за отговорите!

>Според мен по-простата архитектура е по-добра. Тоест винаги когато може REST слоя да работи с домейн обектите.

Ако се връщат Domain обекти, то как да се предпази REST слоят да не върне целият граф на Domain-а?

Поздрави,
Трифон

Daniel Dimov

unread,
Nov 2, 2020, 2:00:39 AM11/2/20
to Bulgarian Java Users Group
С подходящите анотации.

Д

Trifon Trifonov

unread,
Nov 2, 2020, 2:15:27 AM11/2/20
to Bulgarian Java Users Group
>С подходящите анотации.

Това означава, че замърсяваме Domain слоят с информация, която е от DTO/REST слоят. Тоест в Domain слоят трябва да мислим и за неща, които са отговорност на REST или някой друг слой.

Поздрави,
Трифон

Trifon Trifonov

unread,
Nov 2, 2020, 2:18:12 AM11/2/20
to Bulgarian Java Users Group
>Ако е необходим обект с допълнителна/преработена информация - само тогава да се прави преобразуване от домейн обект(и) в DTO, като това преобразуване да става в слоя с бизнес логиката. 

Тоест Бизнес логиката да знае и за Domain и за DTO класовете, и да има методи, които преобразуват от единият в другият?

Поздрави,
Трифон

Doychin Bondzhev

unread,
Nov 2, 2020, 2:43:03 AM11/2/20
to bg-...@googlegroups.com
От доста години гледам да спазвам принципа на разделение на властите. Комуникацията с клиента и бизнес логиката работят с DTO-та. Домейн модела е само да съхранява данните и с него бизнес кода може да работи само за четене ако ще е по-оптимално от гледна точка на производителността.

Основната цел е домейн модела да не е водещ в комуникацията с клиентите. Той само съхранява данните.

Преди години опитах в едно приложение да използвам директно JPA entitys да използвам и в бизнес кода и в презентационния код но това си върви с една купчина усложнения.

Поздрави
--
Получихте това съобщение, защото сте абонирани за групата „Bulgarian Java Users Group“ в Google Групи.
За да се отпишете от тази група и да престанете да получавате имейли от нея, изпратете имейл до bg-jug+un...@googlegroups.com.
За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/4bfa1981-7dbc-4588-94eb-4c943e95c95bn%40googlegroups.com.


-- 
Doychin Bondzhev
dSoft-Bulgaria Ltd.
PowerPro - billing & provisioning solution for Service providers
http://www.dsoft-bg.com/
Mobile: +359888243116
doychin.vcf

Veselin Pavlov

unread,
Nov 2, 2020, 2:58:06 AM11/2/20
to bg-...@googlegroups.com
Здравейте,

С колеги също сме обсъждали доста сървиса трябва ли да знае за ДТО-та и т.н. и от проучването тогава намерихме тази лекция на прагматична clean архитектура представена от Виктор Рентеа: https://www.youtube.com/watch?v=tMHO7_RLxgQ

Най-ключовото за конкретния въпрос, което съм видял, че води до по-чисто разделение на отговорности е, че може да се направи facade layer между controller и service слоя, който се грижи да конвертира от ДТО обекти към домейн обекти и да прави синтактична валидация и да извиква бизнес сървисите. По този начин си пазиш чисти service-ите. 

В този блог пост питахме Виктор конкретни неща свързани с ДТО-та, фасади и т.н. Може да ви е интересно: https://dreamix.eu/blog/newsroom/victor-rentea-x-java-daily

Поздрави,
Веско

За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/d361585d-2a0c-5118-37a1-635a53327cab%40dsoft-bg.com.


--
Veselin Pavlov
Java Expert
 

Daniel Dimov

unread,
Nov 2, 2020, 3:03:29 AM11/2/20
to Bulgarian Java Users Group
> Това означава, че замърсяваме Domain слоят с информация, която е от DTO/REST слоят. Тоест в Domain слоят трябва да мислим и за неща, които са отговорност на REST или някой друг слой.

Не, явно трябваше да отговоря малко по-подробно...

Ще ти дам примера с книгите и авторите. Да кажем, че в обекта Author имаш списък с Book обекти – книгите които този автор е написал. В Book обекта също имаш списък с Author обекти - авторите на тази книга. Ако си помислим по принцип какво бихме искали да имаме като информация при сериализиране на тези два обекта - обекта Author трябва да има само данните на автора (без книгите които е написал), докато обекта Book трябва да има всички характеристики на книгата включително и автора(ите). При положение че тези обекти са анотирани така според нормалната човешка логика - ако това е ОК и за REST слоя - ползваш ги директно. Ако не е ОК - преобразуваш в специализирани DTO.

Д

Емил Гелев

unread,
Nov 2, 2020, 8:04:29 AM11/2/20
to Bulgarian Java Users Group
Привет на всички,

Много хубава тема. Аз лично съм фен на Clean Architecture от Робърт Мартин (Uncle Bob). Виктор вече беше реферирал към неговия блог. Поне в моето съзнание the hexagonal architecture (за която са писали и Виктор и Николай)  си е конкретизация на the clean architecture.
Харесвам концепцията всеки слой да си има портове за комуникация - интерфейси и типове, с които слоя може да работи, а външните слоеве, които искат да го ползват, да си решават дали да конвертират към нещо свое си или да рискуват да зависят на абстракция, която е извън техен контрол.
За мен и базата и REST-a са външни слоеве. Това пази домейна чист от репрезентационни детайли (REST, gRPC, SOAP и т.н) и от изисквания на persistence решението, което сме избрали (SQL, NoSQL, another network (micro)service и т.н.). Предпочитам допълнителния код с конвентирания, но да знам че всеки слой си държи неговата отговорност и си е независим. Така и промени, нови функционалности и refactoring могат да стават парче по парче, без смяната на едно property в DB модела да трябва да се пропагира до REST Controller-a. Точно тези "досадни" конвертирания са границата, която ни дава тази свобода.

Емо.

Ivan St. Ivanov

unread,
Nov 2, 2020, 11:19:55 AM11/2/20
to bg-...@googlegroups.com
Здравейте и от мен!

Аз обикновено при тези слоеве (domain, repo, service, REST controller) имам два слоя обекти: домейн обектите (a.k.a. JPA entities) - Product, и DTOs - ProductDTO.

Service-ите и repository-тата консумират и връщат domain обекти (Product). REST controller-ът съответно консумира и връща на външния свят DTO-та (ProductDTO).

В самият REST controller изолирам mapper клас да преобразува от DTO към domain и обратно. Като в mapper "layer"-а напоследък ползвам MapStruct библиотеката (има я и за Spring и за Jakarta EE / Quarkus).

Поздрави,
Иван

--
Получихте това съобщение, защото сте абонирани за групата „Bulgarian Java Users Group“ в Google Групи.
За да се отпишете от тази група и да престанете да получавате имейли от нея, изпратете имейл до bg-jug+un...@googlegroups.com.
За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/54905baa-6d84-4305-89d1-ce4dca35ed90n%40googlegroups.com.

Trifon Trifonov

unread,
Nov 2, 2020, 3:30:00 PM11/2/20
to Bulgarian Java Users Group
Колеги,

много благодаря за всички отговори! 

>В самият REST controller изолирам mapper клас да преобразува от DTO към domain и обратно. Като в mapper "layer"-а напоследък ползвам MapStruct библиотеката (има я и за Spring и за Jakarta EE / Quarkus).

Аз също ползвам MapStruct, за да преобразувам от Domain към DTO и обратно, но тук отново имам въпрос, който ще публикувам в нова тема..

Поздрави,
Трифон
Reply all
Reply to author
Forward
0 new messages