普通的旧CLR对象与数据传输对象


405

POCO =普通旧CLR(或更好:Class)对象

DTO =数据传输对象

在这篇文章中有一个区别,但是坦率地说,我读过的大多数博客都以定义DTO的方式描述了POCO:DTO是用于在应用程序各层之间移动数据的简单数据容器。

POCO和DTO是同一件事吗?


5
“ POCO =普通旧CLR(或更好:Class)对象”。因此,VB.NET中这种性质的对象也将是POCO,而不是POVO。
J. Polfer

Answers:


568

POCO遵循OOP规则。它应该(但不是必须)具有状态行为。POCO来自POJO,由Martin Fowler [杜撰故事在这里。他使用术语POJO来使拒绝框架繁重的EJB实现更加性感。POCO应该在.Net中的相同上下文中使用。不要让框架决定对象的设计。

DTO的唯一目的是转移状态,并且不应有任何行为。有关使用此模式的示例,请参见Martin Fowler 对DTO解释

区别在于:POCO描述了一种编程方法(老式的面向对象编程),其中DTO是一种模式,用于使用对象“传输数据”。

尽管您可以将POCO像DTO一样对待,但是这样做会冒创建贫血域模型的风险。此外,由于DTO应该被设计为传输数据,而不是代表业务领域的真实结构,因此结构上存在不匹配。这样的结果是DTO往往比您的实际域平坦。

在任何复杂的领域中,创建单独的域POCO并将其转换为DTO几乎总是更好。DDD(域驱动设计)定义了反腐败层此处是另一个链接,但最好的办法是购买书),这是一个很好的结构,可以使隔离清晰。


我知道我在这里经常提到Martin Fowler,但是他创造了POJO一词,并撰写了PoEAA一书,这是DTO的权威参考。
迈克尔·梅多斯

我不确定DTO是否应该有行为。根据Martin Fowler的图判断,DTO可能有行为。
Beatles1692

39
@ Beatles1692,描述的方法是序列化代码。陈述的范围太广,无法说“没有行为”。怎么样“没有业务逻辑”。可以接受序列化代码以及诸如哈希码,相等性和tostring之类的低级对象。
迈克尔·梅多斯

1
@PositiveGuy模型的作用不同于DTO。DTO应该用于将数据从一个域传输到另一个域(无论它们是否在同一运行时中都是无关紧要的)。模型“代表”域的一个方面,例如屏幕,服务或数据源。模型包括状态和行为,它们代表了所建模的内容。
Michael Meadows

2
请注意,贫血的领域模型不一定是不好的,尤其是在您的应用主要是CRUD的情况下。比马丁·福勒(Martin Fowler)更喜欢简单。
Mariusz Jamro

50

因为我已经在博客文章中表达了自己的立场,所以对我来说可能是多余的,但是该文章的最后一段总结了这一点:

因此,总而言之,学习爱POCO,并确保您不会散布任何与DTO相同的错误信息。DTO是简单的数据容器,用于在应用程序的各层之间移动数据。POCO是成熟的业务对象,其一个要求是持久性忽略(没有获取或保存方法)。最后,如果您还没有签出吉米·尼尔森(Jimmy Nilsson)的书,请从您当地的大学图书馆中取书。它具有C#中的示例,是一本好书。

顺便说一句,帕特里克·帕特里克(Patrick)我读了POCO的《生活方式》一文,我完全同意,那是一篇很棒的文章。实际上,这是我推荐的Jimmy Nilsson书中的一部分。我不知道它可以在线获得。他的书确实是我在POCO / DTO /存储库/和其他DDD开发实践中找到的最佳信息来源。


4
链接到博客文章:rlacovara.blogspot.com/2009/03/...
杰米井

28

POCO只是一个不依赖外部框架的对象。是平原。

POCO是否具有行为无关紧要。

DTO可能是POCO,领域对象也可能是POCO(通常行为丰富)。

通常,DTO更有可能出于序列化目的而依赖于外部框架(例如,属性),因为它们通常在系统边界处退出。

在典型的Onion风格的体系结构中(通常在广泛的DDD方法中使用),域层位于中心,因此此时其对象不应在该层之外具有依赖项。



6

我认为DTO可以是POCO。DTO更多关于对象的使用,而POCO更多关于对象的样式(与体系结构概念脱钩)。

当您谈论域模型/业务逻辑模型内部的POCO时,POCO与DTO有所不同的一个例子是,它很好地表示了您的问题域。您可以在整个应用程序中使用POCO,但这可能会带来一些不良的副作用,例如知识泄漏。例如,DTO是从与UI进行通信的服务层使用的,DTO是数据的平面表示,并且仅用于为UI提供数据,并将更改传达回服务层。服务层负责将DTO的两种方式映射到POCO域对象。

更新 Martin Fowler 表示,这种方法要走很繁重的路,只有在域层和用户界面之间存在严重不匹配的情况下才应采用。


2
@David Landman,您包括的链接用于本地DTO模式,这是在系统边界内将DTO用于传输状态时。在这些情况下,您应该非常小心,因为在系统内,您应该已经有一个定义明确的域可以共享。跨系统边界传输状态时,DTO很难避免,并且在所有情况下都非常合适。
Michael Meadows

@Michal Meadows,是的,该链接的确确实是在讨论其他问题。但是我认为,在跨系统边界转移状态的情况下,应该使用翻译服务将一个上下文中的POCO映射到另一上下文中的POCO。还是在系统级别上谈论边界?
戴维·兰德曼

1

DTO的主要用例是从Web服务返回数据。在这种情况下,POCO和DTO是等效的。从Web服务返回POCO后,POCO中的任何行为都将被删除,因此,是否具有行为并不重要。


5
我认为您的回答错误地说明了会发生什么。对于Web服务,将根据对象的公开状态生成代理。这意味着DTO是与POCO分开创建的,而POCO恰好具有与POCO相同的公共状态。它可能看起来很微妙,但这很重要。原因是,即使代理与原始代理完全相同,它实际上也不是从同一类构造的。
Michael Meadows,2009年

嗯不 人们使用DTO在各层(在本例中为Web服务)之间返回/接收数据。人们之所以选择DTO,是因为它只有数据,没有任何行为。的确,代理类也很可能是DTO,并且如果您使用的是POCO类,则会创建一个代理。但是在这种情况下,POCO类实际上是DTO,因为它的行为不会转换。我仍然说使用DTO,因为您将不会错过从未有过的行为。
约翰·桑德斯

5
**语义上:Web服务使用WSDL公开对象状态包。由此产生代理。这些不能包括行为。如果使用Web服务,则您的对象与公开的域对象之间的唯一关系是,它具有基于检查创建的相同公共状态。
Michael Meadows,2009年

7
@约翰,我想你反应过度了。我是说您是对的,但您的措辞具有误导性。“在这种情况下,POCO和DTO是等效的。” 从语义上讲,这是不正确的。POCO可以用作DTO,反之亦然,但这并不意味着它们是等效的……仅相当于汽车和皮卡车的等效物,即使它们都可以用来将您带到杂货店。它们具有重叠的功能,但是即使在杂货店旅行的背景下,您也很难找到可以告诉您的见解等同于F350的人员。
Michael Meadows,

3
这个答案非常错误,Web服务的通用性不足。最重要的是,众所周知,DTO不是POCO。DTO是数据容器,而POCO是作为属性的对象,并且对持久性不了解(没有get或save方法)。
汤姆·斯蒂克

1

这是一般规则:DTO ==邪恶和过度设计的软件的指示器。POCO ==好。“企业”模式破坏了Java EE世界中许多人的大脑。请不要在.NET land中重复该错误。


7
您能详细说明一下吗?从Web服务返回数据时,需要DTO,以避免合同中的实现和平台细节。
约翰·桑德斯

1
是的,John DTO专为您所说的而设计,并且运作良好。但不幸的是,当单层Web应用程序中不需要它们时,它们通常会被使用,并且价值不大。
Craig

9
我想,@ drscroogemcduck,也许您不喜欢DTO,因为DTO被用作第一手段而不是最后手段,但它们并不是天生的邪恶……无非是臭名昭著的单例或工厂模式。架构师将框架推向开发人员的喉咙,迫使他们为所有内容制作DTO,这是邪恶的。对于他们所做的事情,传输数据,DTO(如果审慎地做到)非常适合。
Michael Meadows,2009年

0

DTO类用于对来自不同来源的数据进行序列化/反序列化。当您想从源中反序列化对象时,无论是外部源:服务,文件,数据库等。您可能只想使用其中的一部分,但是您想要一种简单的方法将数据反序列化为宾语。之后,将数据复制到要使用的XModel。串行器是一种加载DTO对象的漂亮技术。为什么?您只需要一个函数即可加载(反序列化)对象。


0

TL; DR:

DTO描述了状态转移的模式。POCO没有描述任何内容。这是在OOP中说“对象”的另一种方式。它来自由马丁·福勒(Martin Fowler)创造的POJO(Java),他从字面上仅将其描述为“对象”的更高级名称,因为“对象”不是很性感。

DTO是一种对象模式,用于在相关层之间转移状态。他们可以有行为(即从技术上讲可以是poco),只要该行为不会使状态发生变化即可。例如,它可能具有序列化自身的方法。

POCO是一个普通的对象,但是“普通”的含义是它并不特殊。这只是意味着它是一个CLR对象,没有隐式模式。通用术语。它不是与其他框架一起使用的。因此[JsonProperty],例如,如果您的POCO的所有属性都具有EF装饰,那么我认为它不是POCO。

这里有一些不同种类的对象模式的例子进行比较:

  • 视图模型:用于为视图数据建模。通常具有数据注释以帮助绑定和验证。在MVVM中,它还充当控制器。不仅仅是DTO
  • 值对象:用于表示值
  • 聚合根:用于管理状态和不变式
  • 处理程序:用于响应事件/消息
  • 属性:用作装饰来处理横切关注点
  • 服务:用于执行复杂的任务
  • 控制器:用于控制请求和响应的流程
  • 工厂:用于配置和/或组装复杂的对象,以在构造函数不够好时使用。还用于确定在运行时需要创建哪些对象。
  • 存储库/ DAO:用于访问数据

这些都是对象,但请注意,它们中的大多数通常与某个模式相关联。因此,您可以将它们称为“对象”,也可以更具体地说明其意图,并按其含义进行称呼。这也是我们拥有设计模式的原因。在一些作品中描述复杂的概念。DTO是一种模式。聚合根是一个模式,视图模型是一个模式(例如MVC和MVVM)。POCO不是模式。

POCO没有描述模式。这只是在OOP中引用类/对象的另一种方式。将其视为一个抽象概念;他们可以指任何东西。IMO,这是一种单向关系,因为一旦对象到达只能清洁地用于一个目的的位置,它就不再是POCO。例如,一旦用装饰标记了您的类以使其可以在某些框架上使用,它就不再是POCO。因此:

  • DTO是POCO
  • POCO不是DTO
  • 视图模型是POCO
  • POCO不是视图模型

区分两者的重点在于保持模式清晰一致,以免引起关注并导致紧密耦合。例如,如果您有一个具有更改状态方法的业务对象,但还使用EF装饰修饰到地狱,以保存到SQL Server和JsonProperty,以便可以通过API端点发送回该对象。该对象将无法容忍更改,并且可能会随处可见属性的各种变体(例如,UserId,UserPk,UserKey,UserGuid,其中一些标记为不保存到数据库,而另一些标记为不序列化到API端点处的JSON)。

因此,如果您要告诉我的是DTO,那么我可能会确保除了将状态转移之外,它从未被用于其他任何用途。如果您告诉我某些东西是视图模型,那么我可能会确保它没有保存到数据库中。如果您告诉我某些东西是域模型,那么我可能会确保它不依赖于域之外的任何东西。但是,如果您告诉我某事是POCO,那么您根本不会告诉我太多。


-13

甚至不称它们为DTO。他们被称为Models..Period。模型永远不会有行为。我不知道是谁提出了这个愚蠢的术语DTO,但是我所能想到的一定是.NET。考虑一下MVC中的视图模型,同样重要的是,模型用于在服务器层之间或整个线路周期之间传输状态,它们都是模型。带有数据的属性。这些是您通过电线的模型。模型,模型模型。而已。

我希望愚蠢的术语“ DTO”会从我们的词汇中消失。


1
我不知道您从哪里想到模型永远不会有行为。您如何在没有建模行为的情况下为CRUD以外的模型建模?甚至ViewModel在许多情况下也具有行为,尤其是在MVVM应用程序中。DTO是一个有用的术语,因为它可以准确地描述目的。传输数据。
杰拉尔德2015年

9
因事实上的不正确和崇高的态度而被谴责。
joedotnot 2015年

废话。模型应该是愚蠢的容器。没有DTO,这是MS的固定期限。您可以在域,服务和应用之间转移模型。期。DTO是不必要的术语浪费,只会使事情更加混乱。模型,仅此而已。模型可能有也可能没有行为。视图模型不应该。该行为应在BL中,而不在Model类中。
PositiveGuy

我同意DTO在功能上是模型。ViewModel具有行为,是您在MVVM中绑定的对象。但是,我写了一个应用程序,其中我的模型更智能(基本上是VM,但我不想调用它们),并且它们“接受”了DTO对象。这使我可以在框架中有更多选择。因此,从CRUD(甚至是EF)中,我将通过WCF服务传输对象,并接收DTO对象并将其封装(添加OnProp Change等)。我的ViewModel执行了进一步的封装,并且可能接受了两个(或列表)“ Model”。严格的定义是VM。
SQLMason

“您在域,服务和应用之间转移模型”为什么对于您描述的这种行为,术语模型比术语DTO更合适和更合适?
ca
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.