普通旧CLR对象vs数据传输对象

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

DTO =数据传输对象

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

POCO和DTO是同一个东西吗?

152852 次浏览

POCO遵循面向对象的规则。它应该(但不必)具有状态而且行为。POCO来自POJO,由Martin Fowler创造[故事在这里]。他使用术语POJO作为一种方式,使其更性感地拒绝框架繁重的EJB实现。在. net中,POCO应该在相同的上下文中使用。不要让框架支配你的对象设计。

DTO的唯一目的是传输状态,不应该有任何行为。参见Martin Fowler的有关“销货权”的解释以获得使用此模式的示例。

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

虽然你可以像对待dto一样对待poco,但如果你这样做,你会有创建贫血域模型的风险。此外,还存在结构上的不匹配,因为dto应该被设计为传输数据,而不是代表业务领域的真实结构。这样做的结果是dto往往比您的实际域更平坦。

在任何合理复杂的域中,最好创建单独的域poco并将它们转换为dto。DDD(领域驱动设计)定义了反腐败层(另一个链接在这里,但最好的方法是买书),这是一个很好的结构,使隔离清晰。

我认为DTO可以是POCO。DTO更多地是关于对象的使用,而POCO更多地是关于对象的风格(与体系结构概念分离)。

POCO与DTO不同的一个例子是,当您在域模型/业务逻辑模型中讨论POCO时,POCO是问题域的一个很好的OO表示。您可以在整个应用程序中使用POCO,但这可能会产生一些不良的副作用,例如知识泄漏。例如,DTO是从与UI通信的服务层使用的,DTO是数据的平面表示,仅用于向UI提供数据,并将更改传达回服务层。服务层负责将DTO的双向映射到POCO域对象。

Martin Fowler指出,这种方法是一条艰难的道路,只有在域层和用户界面之间存在显著不匹配的情况下才应该采用。

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

下面是一般规则:DTO=邪恶和过度工程软件的指示器。少= =好。“企业”模式已经摧毁了Java EE世界中许多人的大脑。请不要在。net领域重复这个错误。

我已经在我的博客文章中阐述了我的立场,所以我在这里发表评论可能是多余的,但那篇文章的最后一段似乎总结了一些事情:

因此,总而言之,要学会热爱POCO,并确保不要传播关于它与DTO是同一件事的任何错误信息。dto是简单的数据容器,用于在应用程序的层之间移动数据。poco是成熟的业务对象,只有一个要求,即它们是Persistence ignorance(没有get或save方法)。最后,如果你还没有看过吉米·尼尔森的书,那就从你当地大学的书库中挑一本吧。它有c#的例子,是一本很棒的读物。

顺便说一句,帕特里克,我把POCO当作一篇生活方式的文章来读,我完全同意,这是一篇很棒的文章。这实际上是我推荐的吉米·尼尔森书中的一个章节。我不知道网上也有。他的书确实是我在POCO / DTO / Repository /和其他DDD开发实践方面找到的最好的信息来源。

POCO只是一个不依赖于外部框架的对象。这是显而易见的。

POCO是否有行为无关紧要。

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

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

在典型的Onion风格架构中(通常在广泛的DDD方法中使用),领域层被放置在中心,因此它的对象在这一点上不应该有该层之外的依赖关系。

甚至不要称它们为dto。它们被称为模型....句号。模特从来没有行为。我不知道是谁想出了这个愚蠢的术语DTO,但它一定是一个。net的东西,这是我所能想到的。想想MVC中的视图模型,同样的东西,模型是用来在层与层之间传递状态的服务器端或线上传输,它们都是模型。属性与数据。这些是你传递的模型。模特,模特。就是这样。

我希望DTO这个愚蠢的术语能从我们的词汇表中消失。

我为此写了一篇文章:DTO vs值对象vs POCO

简而言之:

  • DTO !=值对象
  • Dto⊂poco
  • 价值对象⊂POCO

DTO对象用于将来自不同来源的数据反序列化为对象。这些对象不是您的模型(POCO)对象。您需要将这些对象转换为您的Model (POCO)对象。转换主要是一个复制操作。如果是内部源,您可以直接从源填充那些POCO对象,但如果是外部源,则不建议这样做。外部源具有API,其中包含它们所使用的模式的描述。在DTO中加载请求数据,然后在POCO中转换这些数据,这要容易得多。是的,这是一个额外的步骤,但有一个理由。规则是在对象中加载来自源的数据。可以是JSON, XML等等。加载后,将数据转换为您在模型中需要的数据。所以大多数时候DTO是外部源的对象像。有时您甚至可以获得源提供程序的Schema,然后您可以更容易地反序列化,XML与XSD的工作方式类似。

TL;博士:

DTO描述状态传输的模式。POCO并没有描述太多东西,除了它没有什么特别之处。这是“客体”的另一种说法。在OOP。它来自POJO (Java),由Martin Fowler创造,他只是把它描述为“对象”的一个更花哨的名字,因为“对象”不是很性感,人们都在回避它。

扩大……

好吧,用一种我认为需要的更高尚的方式来解释这个问题,从你最初关于dto的问题开始:

DTO是用于在关注点层之间传递状态的对象模式。它们可以有行为(即技术上可以是poco),只要行为不改变状态。例如,它可能有一个序列化自身的方法。

POCO是普通对象,但所谓“普通”是指它不特殊,没有任何特定的需求或约定。它只是意味着它是一个没有隐含模式的CLR对象。一个通用术语。我还听说它被扩展到不能与其他框架一起工作的事实。因此,如果你的POCO在它的属性上都有[JsonProperty]或EF装饰,那么我认为它不是一个POCO。POCOs就像你在学校里学习创建的对象一样,是自由的、不受阻碍的

这里有一些不同类型的对象模式的例子来比较:

  • 视图模型:用于为视图建模数据。通常有数据注释来帮助绑定和验证特定的视图(即通常不是一个共享对象),或者在这个时代,一个特定的视图组件(例如React)。在MVVM中,它还充当控制器。它不仅仅是DTO;它不是在传递状态,而是在呈现状态,或者更具体地说,以一种对UI有用的方式形成状态。
  • 值对象:用于表示值,应该是不可变的
  • 聚合根:用于管理状态和不变量。应该不允许引用内部实体以外的ID
  • 处理程序:用于响应事件/消息。
  • 属性:用作处理横切关注点的装饰。可能只允许在某些对象级别上使用(例如,属性而不是类,方法而不是属性,等等)。
  • 服务:用于执行复杂任务。通常是某种形式的外观。
  • 控制器:用于控制请求和响应的流。通常限于特定协议或充当某种中介;它负有特殊的责任。
  • 工厂:用于配置和/或组装复杂对象,以便在构造函数不够好时使用。也用于决定需要在运行时创建哪些对象。
  • Repository / DAO:用于访问数据。通常公开CRUD操作或表示数据库模式的对象;可以使用特定于实现的属性进行标记。事实上,其中一个模式DAO对象实际上是另一种DTO……
  • API的合同:可能被序列化属性标记。通常需要有公共getter和setter,并且应该是轻量级的(不是一个过于复杂的图);与序列化无关的方法并不典型,不鼓励使用。

可以将它们仅仅视为对象,但请注意,它们中的大多数通常绑定到一个模式或具有隐含的限制。所以你可以称它们为“对象”。或者你可以更具体地说明它的意图,并根据它是什么来称呼它。这也是为什么我们有设计模式;用几句话描述复杂的概念。DTO是一种模式。聚合根是一个模式,视图模型是一个模式(例如MVC &MVVM)。

POCO不描述模式。它只是OOP中引用类/对象的另一种方式,可以是任何东西。把它看作一个抽象的概念;它们可以指任何东西。在我看来,这是一种单向关系,因为一旦一个对象达到了只能干净地服务于一个目的的地步,它就不再是POCO了。例如,一旦你用装饰标记了你的类,使它与一些框架(即。“工具化”它),它不再是POCO。因此我认为有一些逻辑关系:

  • DTO是POCO(直到它被插入)
  • POCO可能不是DTO
  • 视图模型是一个POCO(直到它被仪器化)
  • POCO可能不是视图模型

区分两者的关键在于保持模式的清晰和一致,以避免交叉关注点并导致紧密耦合。例如,如果你有一个业务对象,它有方法来改变状态,但也用EF装饰来保存到SQL Server和JsonProperty,以便它可以通过API端点发送回。该对象将不允许更改,并且可能会充斥着属性的变体(例如UserId, UserPk, UserKey, UserGuid,其中一些被标记为不能保存到DB,而另一些被标记为不能在API端点序列化为JSON)。

因此,如果您告诉我某个东西是DTO,那么我可能会确保它只用于移动状态之外的任何事情。如果你告诉我某个东西是视图模型,那么我可能会确保它不会被保存到数据库中。如果您告诉我某个东西是域模型,那么我可能会确保它不依赖于域之外的任何东西。但是如果您告诉我某个东西是POCO,那么除了它不是也不应该被工具化之外,您实际上并没有告诉我太多。

历史

转述Fowler的解释:在一个对象很花哨的世界里(例如,遵循特定的模式,有仪器等),它在某种程度上鼓励人们避免使用不花哨的对象来捕获业务逻辑。所以他们给它起了个花哨的名字POJO。如果你想要一个例子,他指的是一个“实体豆”。这是一种具有非常具体的约定和要求的对象。如果你不知道那是什么——>Java bean

相比之下,POJO/POCO只是您在学校学习创建的常规ole对象。