Pull to refresh

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. По-хорошему, должна была быть аннотация вроде SurrogateId, применяемая к классу.

В принципе оно нормально работает, вот только не очень удобно во всяких remote facade таскать этот id в presentation, т.к. для этого нужно дополнительно инжектить репозиторий. С другой стороны известно, что инжектить нужно не очень много, а если пришлось инжектить много зависимостей — у вас неправильный дизайн.
На самом деле, POCO все же обычно отличают от DTO, потому что POCO это не просто там класс, а класс моделирующий домен. Он содержит в себе не только данные но и поведение. Это принципиальное отличие см например статью об анемичной доменной модели.
Это позднее наслоение. POCO — это действительно просто plain old C(#) class, в противовес тяжеловесному наследнику чего угодно.
Не class а Object. И это отсылка на ООП которое не привествует объекты без поведения.
Object, да, задумался. А вот по поводу отсылки не уверен я.
Я тут нагуглил вопрос на StackOverflow:
смотреть ответы.

Там есть принятый сообществом ответ и непринятый сообществом ответ, который ссылается на англоязычную версию этой статьи.
На самом деле

Что-то не вижу как сказанное вами противоречит тому, что написано в статье
Тем что множество DTO не входит во множество POCO.
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 — это термин из области реализации, означающий, что объект не наследует от какого-нибудь 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# объект» тут вполне в тему и звучит более по-русски
Если вдаваться в трудности перевода, то перевод слова 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 у вас не пересекаются. Скажем, возьмём банальную точку:

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) не должны знать о том, как они сериализуются. В более-менее сложных случаях этого трудно достичь если сериализовывать их напрямую.
Да, иначе придется конфигрурировать их на сериализацию, через теже атрибуты, например. Что как бы вообще не туда.
Sign up to leave a comment.

Articles