Простой старый объект CLR против объекта передачи данных

405

POCO = Простой старый объект CLR (или лучше: класс)

DTO = Объект передачи данных

В этом посте есть разница, но, честно говоря, большинство блогов, которые я читал, описывают POCO в том, как определяется DTO: DTO - это простые контейнеры данных, используемые для перемещения данных между уровнями приложения.

POCO и DTO - это одно и то же?

Патрик Питерс
источник
5
«POCO = Обычный старый объект CLR (или лучше: Класс)». Таким образом, объекты такого типа в VB.NET также будут POCO, а не POVO.
Дж. Полфер

Ответы:

568

POCO следует правилам ООП. Он должен (но не должен) иметь состояние и поведение. POCO происходит от POJO, придуманного Мартином Фаулером [ анекдот здесь ]. Он использовал термин POJO как способ сделать его более сексуальным, чтобы отвергнуть тяжелые реализации EJB. POCO следует использовать в том же контексте в .Net. Не позволяйте фреймворкам определять дизайн вашего объекта.

Единственная цель DTO - передать состояние и не должна иметь никакого поведения. См. Объяснение DTO Мартина Фаулера для примера использования этого шаблона.

Вот разница: POCO описывает подход к программированию (старомодное объектно-ориентированное программирование), где DTO - это шаблон, который используется для «передачи данных» с использованием объектов.

Хотя вы можете рассматривать POCO как DTO, вы рискуете создать анемичную модель домена, если вы это сделаете. Кроме того, существует несоответствие в структуре, поскольку DTO должны быть предназначены для передачи данных, а не для представления истинной структуры бизнес-сферы. Результатом этого является то, что DTO имеют тенденцию быть более плоскими, чем ваш реальный домен.

В домене любой разумной сложности вам почти всегда лучше создавать отдельные доменные POCO и переводить их в DTO. DDD (управляемый доменом дизайн) определяет антикоррупционный уровень (еще одна ссылка здесь , но лучше всего купить книгу ), которая является хорошей структурой, которая делает сегрегацию ясной.

Майкл Медоуз
источник
Я знаю, что много ссылался на Мартина Фаулера здесь, но он придумал термин POJO и написал книгу PoEAA, которая является окончательной ссылкой на DTO.
Майкл Медоус
Я не уверен, что DTO не должен иметь поведения. Судя по диаграмме Мартина Фаулера, DTO может иметь поведение.
Beatles1692
39
@ Beatles1692, изображенные методы являются кодом сериализации. Это, вероятно, слишком широкое утверждение, чтобы сказать «без поведения». Как насчет "нет бизнес-логики". Код сериализации и низкоуровневые объекты, такие как хэш-код, равенство и tostring, должны быть приемлемыми.
Майкл Медоус
1
@PositiveGuy Модель служит для целей, отличных от DTO. DTO должен использоваться для передачи данных из одного домена в другой (независимо от того, находятся ли они в одной среде выполнения или нет). Модель «представляет» аспект домена, такой как экран, сервис или источник данных. Модели включают состояние и поведение, которые представляют то, что они моделируют.
Майкл Медоус
2
Обратите внимание, что анемичные доменные модели не обязательно являются плохими, особенно если ваше приложение в основном CRUD. Пользуйтесь простотой над Мартином Фаулером.
Мариуш Джамро
50

Возможно, для меня излишне вносить свой вклад, так как я уже изложил свою позицию в своей статье в блоге, но последний абзац этой статьи подводит итог:

Итак, в заключение, научитесь любить POCO и убедитесь, что вы не распространяете дезинформацию о том, что это то же самое, что DTO. DTO - это простые контейнеры данных, используемые для перемещения данных между уровнями приложения. POCO - это полноценные бизнес-объекты с одним требованием, чтобы они оставались невосприимчивыми к постоянству (нет методов get или save). И наконец, если вы еще не проверили книгу Джимми Нильссона, возьмите ее из местных стеков университетов. У него есть примеры на C #, и это отлично читается.

Кстати, Патрик Я прочитал POCO как статью о стиле жизни, и я полностью согласен, что это фантастическая статья. На самом деле это раздел из книги Джимми Нильссона, который я рекомендовал. Я понятия не имел, что это было доступно онлайн. Его книга действительно является лучшим источником информации, которую я нашел в POCO / DTO / Repository / и других методах разработки DDD.


источник
4
Ссылка на статью в блоге: rlacovara.blogspot.com/2009/03/…
Джейми Иде
28

POCO - это просто объект, который не зависит от внешней среды. Это PLAIN.

Неважно, имеет ли POCO поведение или нет.

DTO может быть POCO, как и объект домена (который, как правило, имеет богатое поведение).

Как правило, DTO с большей вероятностью принимают зависимости от внешних структур (например, атрибутов) для целей сериализации, поскольку обычно они выходят на границу системы.

В типичных архитектурах в стиле Onion (часто используемых в широком подходе DDD) уровень домена размещается в центре, и поэтому его объекты не должны иметь зависимостей вне этого уровня.

Нил
источник
6

Я думаю, что DTO может быть POCO. DTO больше относится к использованию объекта, в то время как POCO больше к стилю объекта (отделенного от архитектурных концепций).

Один из примеров, когда POCO отличается от DTO, - это когда вы говорите о POCO внутри модели вашего домена / модели бизнес-логики, которая является хорошим представлением OO вашего проблемного домена. Вы можете использовать POCO во всем приложении, но это может привести к нежелательным побочным эффектам, таким как утечка знаний. DTO, например, используются на сервисном уровне, с которым взаимодействует пользовательский интерфейс, DTO представляют собой плоское представление данных и используются только для предоставления пользовательскому интерфейсу данных и передачи изменений обратно на сервисный уровень. Уровень обслуживания отвечает за сопоставление обоих направлений DTO с объектами домена POCO.

Обновление Мартин Фаулер сказал , что этот подход является тяжелым дорога принять, и должен быть принят только при наличии значительного несоответствия между доменным слоем и пользовательским интерфейсом.

Дэви Лэндман
источник
2
@ Дэвид Лэндман, ссылка, которую вы включили, предназначена для шаблона Local DTO, когда DTO используются для передачи состояния в пределах вашей системы. В этих случаях вы должны быть очень осторожны, так как в вашей системе у вас уже должен быть четко определенный домен, который можно использовать совместно. При передаче состояния через системные границы DTO трудно избежать, и оно вполне уместно во всех случаях.
Майкл Медоуз
@Michal Meadows, да, ссылка действительно говорит о другом подмножестве проблем. Но я думаю, что в случае передачи состояния через системную границу вы должны использовать службу перевода, чтобы отобразить POCO из одного контекста в POCO из другого контекста. Или вы говорите о границах на системном уровне?
Дэви Лэндман
1

Основной вариант использования DTO - возврат данных из веб-службы. В этом случае POCO и DTO эквивалентны. Любое поведение в POCO будет удалено при его возврате из веб-службы, поэтому не имеет значения, имеет ли оно поведение.

Джон Сондерс
источник
5
Я думаю, что ваш ответ искажает то, что происходит немного. В случае веб-службы прокси генерируется на основе выставленного состояния объекта. Это означает, что DTO создается отдельно от POCO, у которого просто такое же публичное состояние, что и у POCO. Это может показаться тонким, но это важно. Причина в том, что даже если прокси-сервер идентичен оригиналу, он фактически не создан из того же класса.
Майкл Мидоуз
Нет Один использует DTO для возврата / получения данных между уровнями, в данном случае это веб-сервис. Каждый выбирает DTO, потому что у него есть только данные и нет поведения. Это правда, что прокси-класс, скорее всего, также будет DTO, и что если бы вы использовали класс POCO вместо этого, то прокси был бы создан. Но в этом случае класс POCO фактически является DTO, поскольку его поведение не будет преобразовываться. Я все еще говорю, используйте DTO, потому что вы не пропустите поведение, которое никогда не существовало.
Джон Сондерс
5
** Семантически: веб-службы предоставляют пакеты состояния объекта с использованием WSDL. Прокси генерируются из них. Они не могут включать в себя поведение. При использовании веб-службы единственное отношение между вашим объектом и открытым объектом домена состоит в том, что он имеет то же открытое состояние, созданное на основе проверки.
Майкл Мидоуз
7
@ Джон, я думаю, ты слишком остро реагируешь. Я говорю, что вы правы, но ваша формулировка вводит в заблуждение. «В этом случае POCO и DTO эквивалентны». Семантически это не так. POCO можно использовать в качестве DTO и наоборот, но это не значит, что они эквивалентны ... не более чем автомобиль и пикап эквивалентны, даже если они оба могут быть использованы для доставки вас в продуктовый магазин. Они имеют перекрывающуюся функцию, но вам будет сложно найти кого-то, кто скажет вам, что понимание эквивалентно F350, даже в контексте продуктовой поездки.
Майкл Мидоуз
3
Этот ответ очень неправильный, веб-сервис недостаточно универсален. Самое главное, это хорошо установленный факт, что DTO НЕ POCO. DTO - это контейнер данных, в то время как POCO являются объектами как свойствами и неосведомлены о постоянстве (нет методов get или save).
Том Стиккель
1

Вот общее правило: DTO == зло и индикатор чрезмерно спроектированного программного обеспечения. Poco == хорошо. «корпоративные» шаблоны разрушили мозги многих людей в мире Java EE. Пожалуйста, не повторяйте ошибку в .NET Land.

benmmurphy
источник
7
Не могли бы вы уточнить, пожалуйста? DTO требуются при возврате данных из веб-службы, чтобы избежать реализации и особенностей платформы в контракте.
Джон Сондерс
1
Да, John DTO предназначены для того, что вы говорите, и работают хорошо. Но, к сожалению, они часто используются, когда они не требуются в одноуровневых веб-приложениях, и имеют небольшую ценность.
Крейг,
9
Я думаю, @drscroogemcduck, что, возможно, вам не нравятся DTO, потому что они используются в качестве первого средства, а не в качестве последнего средства, но они не являются изначально злыми ... не более, чем печально известные синглтоны или фабричные паттерны. Зло в том, что архитекторы пихают фреймворки в горло разработчиков, заставляя их создавать DTO для всего. Для передачи данных DTO (если это сделано разумно) идеально подходят.
Майкл Мидоуз
0

Классы DTO используются для сериализации / десериализации данных из разных источников. Если вы хотите десериализовать объект из источника, не имеет значения, какой это внешний источник: сервис, файл, база данных и т. Д., Возможно, вы захотите использовать только некоторую его часть, но вам нужен простой способ десериализации этих данных в объект. после этого вы копируете эти данные в модель XModel, которую хотите использовать. Сериализатор - это красивая технология для загрузки объектов DTO. Почему? вам нужна только одна функция для загрузки (десериализации) объекта.

Герман ван дер Блом
источник
0

TL; DR:

DTO описывает схему передачи государства. POCO ничего не описывает. Это еще один способ сказать «объект» в ООП. Это происходит от POJO (Java), придуманного Мартином Фаулером, который буквально описывает его как причудливое имя для «объекта», потому что «объект» не очень сексуален.

DTO - это объектный паттерн, используемый для передачи состояния между интересующими слоями. Они могут иметь поведение (то есть технически может быть poco), пока это поведение не изменяет состояние. Например, у него может быть метод, который сериализует себя.

POCO - это простой объект, но под «простым» подразумевается то, что он не является особенным. Это просто означает, что это объект CLR без какого-либо подразумеваемого шаблона. Общий термин. Он не предназначен для работы с другими фреймворками. Так что, если ваш POCO имеет [JsonProperty]или EF украшения во всех своих свойствах, например, то я бы сказал, что это не POCO.

Вот несколько примеров различных типов объектов для сравнения:

  • Модель представления : используется для моделирования данных для представления. Обычно имеет аннотации данных для облегчения привязки и проверки. В MVVM он также действует как контроллер. Это больше, чем DTO
  • Value Object : используется для представления значений
  • Aggregate Root : используется для управления состоянием и инвариантами
  • Обработчики : используются для ответа на событие / сообщение
  • Атрибуты : используется в качестве украшения для решения сквозных проблем
  • Сервис : используется для выполнения сложных задач
  • Контроллер : используется для управления потоком запросов и ответов
  • Фабрика : используется для настройки и / или сборки сложных объектов для использования, когда конструктор недостаточно хорош. Также используется для принятия решения о том, какие объекты должны быть созданы во время выполнения.
  • Репозиторий / DAO : используется для доступа к данным

Все это просто объекты, но обратите внимание, что большинство из них, как правило, привязаны к шаблону. Таким образом, вы могли бы назвать их «объектами» или вы могли бы быть более точными в отношении его намерений и называть его так, как оно есть. Это также, почему у нас есть шаблоны проектирования; описать сложные концепции в нескольких работах. DTO это шаблон. Агрегированный корень - это шаблон, View Model - шаблон (например, MVC и MVVM). POCO это не шаблон.

POCO не описывает шаблон. Это просто другой способ ссылки на классы / объекты в ООП. Думайте об этом как об абстрактном понятии; они могут относиться к чему угодно. ИМО, хотя есть односторонние отношения, потому что, как только объект достигает точки, когда он может чисто служить только одной цели, он больше не является POCO. Например, как только вы пометите свой класс украшениями, чтобы он работал с какой-то структурой, он больше не будет POCO. Следовательно:

  • DTO - это POCO
  • POCO - это не DTO
  • Модель представления - это POCO
  • POCO - это не модель представления

Смысл разграничения между ними заключается в том, чтобы сохранить четкость и последовательность шаблонов, чтобы не допустить пересечения проблем и привести к тесной взаимосвязи. Например, если у вас есть бизнес-объект, который имеет методы для изменения состояния, но он также украшен украшениями EF для сохранения в SQL Server и JsonProperty, чтобы его можно было отправить обратно через конечную точку API. Этот объект будет нетерпим к изменению и, скорее всего, будет завален вариантами свойств (например, UserId, UserPk, UserKey, UserGuid, где некоторые из них помечены так, что они не сохранены в БД, а другие помечены как не сериализованные для JSON в конечной точке API).

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

Sinaesthetic
источник
-13

Даже не называй их DTO. Они называются моделями .... Период. Модели никогда не имеют поведения. Я не знаю, кто придумал этот тупой термин DTO, но это, наверное, вещь .NET - это все, что я могу понять. Подумайте о моделях представления в MVC, то же самое, черт возьми **, модели используются для передачи состояния между слоями на стороне сервера или в течение периода передачи, все они модели. Свойства с данными. Это модели, которые вы передаете по проводу. Модели, Модели Модели. Вот и все.

Я хотел бы, чтобы глупый термин DTO отошел от нашего словаря.

PositiveGuy
источник
1
я не знаю, откуда у тебя такая мысль, что у моделей никогда не бывает поведения. Как вы моделируете что-либо кроме CRUD без моделирования поведения? Даже ViewModels имеют поведение во многих случаях, особенно в приложениях MVVM. DTO - полезный термин, потому что он точно описывает цель; передавать данные.
Джеральд
9
понизили за то, что были фактически неправильными, и для понтификационного отношения.
joedotnot
Бред какой то. Модели должны быть тупыми контейнерами. Нет DTO, это выдуманный термин MS. Вы переносите модели между доменами, сервисами и приложениями. Период. DTO - это пустая трата термина, которая не нужна и только еще больше сбивает с толку. Модели, Модели и многое другое Модели, вот и все. Модели могут иметь или не иметь поведение. Просмотр моделей не должен. Такое поведение должно быть в BL, а не в классе Model.
PositiveGuy
Я согласен с тем, что DTO являются функционально моделями. ViewModels имеют поведение и это то, что вы связываете в MVVM. ОДНАКО, я написал приложение, в котором мои Модели были более интеллектуальными (в основном виртуальные машины, но я не хотел их называть), и они "приняли" объект DTO. Это позволило мне иметь больше возможностей в рамках. Таким образом, из CRUD (или даже EF) я передал бы объект через службы WCF, получил бы объект DTO и инкапсулировал его (добавление OnProp Change и т. Д.). Мои ViewModels выполнили дальнейшую инкапсуляцию и, возможно, приняли две (или список) «Моделей». Жесткое определение будет виртуальных машин.
SQLMason
«Вы передаете модели между доменами, службами и приложениями». Почему вы считаете, что термин «модель» более подходит и подходит, чем термин «DTO», для такого поведения, которое вы описываете?
около