Comments 35
в то время как две сущности отличаются друг от друга даже в случае если их свойства полностью совпадают.
вообще Entity подразумевают уникальный ключ. Если и он совпадает то это таки одна сущность. По крайней мере одна бизнес-сущность.
Суррогатные ключи часто не считают свойством сущности, а натуральных может и не быть.
Можно даже сказать, что суррогатный ключ в каком-то виде — протекающая абстракция.
В каком-то смысле да. Обычно, в Java я делаю следующее: для поля id не делаю публичных getter/setter, добавляю коммент, что это костыль для JPA/Hibernate. А делаю я так вот из каких соображений. em.persist говорит, что entity должен быть persistent. При этом реальная синхронизация с БД может произойти несколько позже. А значит, мы получим поведение: id, который был null внезапно становится не null. WTF? Более того, для id нельзя делать сеттер, иначе какой он тогда id? Тем более WTF.
Согласно принципу минимизации WFT я делаю суррогатный ключ свойством репозитория. Т.е. у репозитория имеются методы getId(EntityType), findById(int id). Первый тащит id с помощью PersistentUnitUtil. По-хорошему, должна была быть аннотация вроде
В принципе оно нормально работает, вот только не очень удобно во всяких remote facade таскать этот id в presentation, т.к. для этого нужно дополнительно инжектить репозиторий. С другой стороны известно, что инжектить нужно не очень много, а если пришлось инжектить много зависимостей — у вас неправильный дизайн.
Согласно принципу минимизации WFT я делаю суррогатный ключ свойством репозитория. Т.е. у репозитория имеются методы getId(EntityType), findById(int id). Первый тащит id с помощью PersistentUnitUtil. По-хорошему, должна была быть аннотация вроде
SurrogateId
, применяемая к классу.В принципе оно нормально работает, вот только не очень удобно во всяких remote facade таскать этот id в presentation, т.к. для этого нужно дополнительно инжектить репозиторий. С другой стороны известно, что инжектить нужно не очень много, а если пришлось инжектить много зависимостей — у вас неправильный дизайн.
На самом деле, POCO все же обычно отличают от DTO, потому что POCO это не просто там класс, а класс моделирующий домен. Он содержит в себе не только данные но и поведение. Это принципиальное отличие см например статью об анемичной доменной модели.
Это позднее наслоение. POCO — это действительно просто plain old C(#) class, в противовес тяжеловесному наследнику чего угодно.
Не class а Object. И это отсылка на ООП которое не привествует объекты без поведения.
Object, да, задумался. А вот по поводу отсылки не уверен я.
Я тут нагуглил вопрос на StackOverflow:
смотреть ответы.
Там есть принятый сообществом ответ и непринятый сообществом ответ, который ссылается на англоязычную версию этой статьи.
смотреть ответы.
Там есть принятый сообществом ответ и непринятый сообществом ответ, который ссылается на англоязычную версию этой статьи.
Ну кстати да, принятый ответ очень хорош.
На самом деле
Что-то не вижу как сказанное вами противоречит тому, что написано в статье
Value Object-ы могут содержать логику
Мне всегда казалось, что Value Object — простые иммутабельные объекты типа дат или денег. Никакой логики в них быть не должно, заисключением, разве что, логики сравнения и определения равества. Они как бы строительные кирпичики для Entity, Business Objects, DTO и пр.
У Вас в кругах Эйлера так и нарисовано Value Object ⊂ POCO. Какой же это Plain Object, если он с логикой? POCO (POJO, PODS) — это принцип организации данных, пассивные структуры. DTO — это роль, которую данные выполняют (Transfer).
А почему POCO (POJO) не может содержать логику?
Вопрос в том, что такое логика.
Если рассматривать например даты, то не вижу проблем добавить методов работы с самими датами.
Но если брать какой-нибудь объект типа «товар», неужели это нормально создавать класс в котором будут методы по добавлению товаров в БД, отправки их по сети, получения связанных сущностей типа всех покупателей? Не будет ли это перегружать класс, и создавать проблем с тестированием, распараллеливанием, поддержкой?
Если рассматривать например даты, то не вижу проблем добавить методов работы с самими датами.
Но если брать какой-нибудь объект типа «товар», неужели это нормально создавать класс в котором будут методы по добавлению товаров в БД, отправки их по сети, получения связанных сущностей типа всех покупателей? Не будет ли это перегружать класс, и создавать проблем с тестированием, распараллеливанием, поддержкой?
То, что вы описываете — это нарушение SRP, и не является специфичной особенностью value types (собственно, вы и пример-то приводите от сущности).
Извиняюсь за глупые вопросы. Возможно, автор статьи (@vkhorikov) подскажет.
Но можно привести пример POCO с логикой, чтоб он не нарушал SRP, и не был VO?
Будет ли единственным отличием POCO и VO иммутабельность последнего?
Можно ли вообще обойтись без термина POCO, ограничившись DTO (объекты без логики) и VO (объекты с тривиальной логикой)?
Но можно привести пример POCO с логикой, чтоб он не нарушал SRP, и не был VO?
Будет ли единственным отличием POCO и VO иммутабельность последнего?
Можно ли вообще обойтись без термина POCO, ограничившись DTO (объекты без логики) и VO (объекты с тривиальной логикой)?
Но можно привести пример POCO с логикой, чтоб он не нарушал SRP, и не был VO?
Любая бизнес-сущность.
Вы смешиваете объекты, находящиеся в разных областях. POCO — это термин из области реализации, означающий, что объект не наследует от какого-нибудь
Component
или Entity
, навязанного инфраструктурой. Value Object и Entity — это термины из области моделирования (например, DDD), и они означают разделение объектов в модели на имеющие свою идентичность (сущности), и не имеющие таковой (объекты-значения).методы по добавлению товаров в БД
Нет, добавление товаров в БД — это не часть бизнес-логики, а часть инфраструктуры. См. паттерн repository
отправки их по сети
Если отправка осуществляется путём выставления какого-нибудь REST API, то это часть presentation layer, который выше слоем над domain model. Если задача обратная — надо постучать в удалённый сервис, например, по SOAP, то пишется интерфейс ProductConsumer, реализация которого может его отправлять куда и как угодно (и скорее всего, даже не пишется вручную, а генерится каким-нибудь CXF или Jersey). При этом сам класс Product вполне может содержать методы, которые решают, когда товар отправлять и нужно ли его вообще отправлять, что уже является частью бизнес-логики (потому что такие вещи формулируются заказчиком).
получения связанных сущностей типа всех покупателей
А вот это пожалуйста. В JPA есть аннотация ManyToMany. При этом это не будет «логикой», это делает JPA-провайдер (который является частью инфраструктуры), а внутри — просто Set, Map или List.
Простите, но все три определения ввел в обиход Мартин Фаулер. И он в них вложил вполне определенный смысл. POCO — если переводить на русский «Старый добрый C# объект» объекты в объектно-ориентированном программировании содержат не только данные но и поведение.
Не знал, что Plain переводится как «старый» :)
Не поверите, как старый переводится Old. Plain Old C#(или CLR) Object.
Если переводить пословно получится что-то типа
«Обыкновенный старый C# объект». Но, учитывая что вводилось это понятие как клевое название для обычных классических объектов, перевод «Старый добрый C# объект» тут вполне в тему и звучит более по-русски
«Обыкновенный старый C# объект». Но, учитывая что вводилось это понятие как клевое название для обычных классических объектов, перевод «Старый добрый C# объект» тут вполне в тему и звучит более по-русски
Если вдаваться в трудности перевода, то перевод слова Plain как «Плоский», более отражает суть и поведение объекта — простой и без наследования.
Плоский объект для меня — добрый объект :) Кому как удобнее :)
Плоский объект для меня — добрый объект :) Кому как удобнее :)
более отражает суть и поведение объекта — простой и без наследования.
Так наследование-то там вполне может быть.
А автор не вкладывал сюда значение «плоский». Он вкладывал значение обычный (то есть не навязанный никаким фреймворком). При этом если доменная модель предполагает для конкретного объекта наследование — наследование будет, просто это наследование должно быть по причине такого домена, а не потому что наш фреймворк требует чтобы все сущности были отнаследованы от абстрактного класса Component.
не знаю, чем я думал :) хотел написать «добрый» :) типа Plain переводится как «добрый».
Мнение, которое вы высказали, и послужило причиной написания этой статьи.
Value Object — иммутабельные объектыЭто верно.
Они как бы строительные кирпичики для EntityЭто тоже верно.
Никакой логики в них быть не должноВот это уже неверно. Value Object-ы вполне себе могут содержать логику. Более того, Эванс и ко рекомендуют помещать доменную логику именно в Value Object-ы там где это возможно, т.к. работать с ними проще из-за их неизменяемости. Хороший пример Value Object-а — DateTime в .NET. Неизменямый тип данных с большим количеством логики внутри.
POCO (POJO, PODS) — это принцип организации данныхPOCO — это в первую очередь принцип, который говорит о том, что при моделировании предметной области следует использовать настолько простые классы, насколько возможно. Это понятие никак не связано с DTO (кроме того, что DTO тоже не следует наследовать от тяжеловесных компонент).
На StackOverflow закинули ссылку на вашу статью, родилось обсуждение Наглядный пример различия DTO, POCO (POJO) и Value Object.
В целом я с вашей статьёй согласен, но не пойму, почему на диаграмме Венна DTO и VO у вас не пересекаются. Скажем, возьмём банальную точку:
(По вкусу заменить r/w поля на r/w свойства, r/o поля, r/o свойства.)
POCO? Ну да, куда уж плейн-олдее, одни поля. DTO? Ну да, можно передавать туда-сюда, сериализовать. VO? Ну да, сравнивается и копируется по значению.
В целом я с вашей статьёй согласен, но не пойму, почему на диаграмме Венна DTO и VO у вас не пересекаются. Скажем, возьмём банальную точку:
struct Point {
public int X;
public int Y;
}
(По вкусу заменить r/w поля на r/w свойства, r/o поля, r/o свойства.)
POCO? Ну да, куда уж плейн-олдее, одни поля. DTO? Ну да, можно передавать туда-сюда, сериализовать. VO? Ну да, сравнивается и копируется по значению.
Как правило, Value Object-ы (так же как и Entities) не используются для передачи данных между приложениями. В целом, в простых случаях (как например в вашем) Value Object-ы действительно можно использовать как DTO, но опять же — это до тех пор, пока в них не скопилось достаточно логики.
Хорошей практикой считается изолировать домен приложения. На практике это означает, что доменные классы (Entities and Value Objects) не должны знать о том, как они сериализуются. В более-менее сложных случаях этого трудно достичь если сериализовывать их напрямую.
Хорошей практикой считается изолировать домен приложения. На практике это означает, что доменные классы (Entities and Value Objects) не должны знать о том, как они сериализуются. В более-менее сложных случаях этого трудно достичь если сериализовывать их напрямую.
Хм, либо я вас не так понял либо Martin Fowler не прав.
Sign up to leave a comment.
DTO vs POCO vs Value Object