422

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

DTO = 数据传输对象

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

POCO 和 DTO 是一回事吗?

4

10 回答 10

584

POCO 遵循 OOP 的规则。它应该(但不是必须)具有状态行为。POCO 来自 POJO,由 Martin Fowler [这里的轶事] 创造。他使用 POJO 一词作为一种方式,使拒绝框架重 EJB 实现更加性感。POCO 应该在 .Net 中的相同上下文中使用。不要让框架支配你的对象的设计。

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

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

虽然您可以将 POCO 视为 DTO,但如果这样做,您将面临创建贫血域模型的风险。此外,结构不匹配,因为 DTO 应该设计为传输数据,而不是代表业务领域的真实结构。这样做的结果是 DTO 往往比您的实际域更平坦。

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

于 2009-04-07T12:11:09.020 回答
53

因为我已经在我的博客文章中表明了我的立场,所以我贡献可能是多余的,但那篇文章的最后一段总结了一些事情:

因此,总而言之,要学会爱上 POCO,并确保您不会传播任何关于它与 DTO 相同的错误信息。DTO 是简单的数据容器,用于在应用程序的各层之间移动数据。POCO 是成熟的业务对象,唯一的要求是它们是 Persistence Ignorant(没有获取或保存方法)。最后,如果您还没有查看 Jimmy Nilsson 的书,请从您当地的大学书库中挑选。它有 C# 中的示例,非常适合阅读。

顺便说一句,Patrick,我将 POCO 作为生活方式文章阅读,我完全同意,这是一篇很棒的文章。这实际上是我推荐的 Jimmy Nilsson 书中的一部分。我不知道它可以在线获得。他的书确实是我在 POCO / DTO / Repository / 和其他 DDD 开发实践中找到的最佳信息来源。

于 2009-04-09T02:32:51.173 回答
28

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

POCO 是否有行为无关紧要。

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

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

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

于 2009-11-03T14:14:29.263 回答
16

我为该主题写了一篇文章:DTO vs Value Object vs POCO

简而言之:

  • DTO != 值对象
  • DTO ⊂ POCO
  • 值对象⊂ POCO
于 2015-04-13T17:55:29.380 回答
6

我认为 DTO 可以是 POCO。DTO 更多的是关于对象的使用,而 POCO 更多的是对象的样式(与架构概念解耦)。

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

更新Martin Fowler表示,这种方法是一条繁重的道路,只有在领域层和用户界面之间存在严重不匹配时才应采用。

于 2009-04-07T12:16:50.117 回答
4

TL;博士:

DTO 描述了状态转移的模式。POCO 并没有描述任何东西,只是它没有什么特别之处。这是 OOP 中“对象”的另一种说法。它来自 POJO (Java),由 Martin Fowler 创造,他实际上只是将其描述为“对象”的一个更高级的名称,因为“对象”不是很性感,人们一直在避免它。

扩大...

好的,以我认为需要的更高级的方式来解释这一点,从您最初关于 DTO 的问题开始:

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

POCO 是一个普通的对象,但“普通”的意思是它不是特殊的,没有任何特定的要求或约定。它只是意味着它是一个没有隐含模式的 CLR 对象。一个通用术语。我还听说它扩展为描述它也不能与其他框架一起使用的事实。因此,例如,如果您的 POCO[JsonProperty]在其属性中具有或 EF 装饰,那么我认为它不是 POCO。POCO 像您在学校学习创建的对象一样免费且不受阻碍

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

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

这些可以被视为只是对象,但请注意,它们中的大多数通常都与模式相关联或具有隐含的限制。所以你可以称它们为“对象”,或者你可以更具体地了解它的意图并用它来称呼它。这也是我们有设计模式的原因;用几句话来描述复杂的概念。DTO 是一种模式。聚合根是一种模式,视图模型是一种模式(例如 MVC 和 MVVM)。

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

  • DTO 是 POCO(直到它被检测)
  • POCO 可能不是 DTO
  • 视图模型是一个 POCO(直到它被检测)
  • POCO 可能不是视图模型

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

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

历史

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

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

于 2019-08-04T22:53:19.677 回答
1

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

于 2009-04-07T12:41:58.510 回答
1

DTO 对象用于将数据反序列化为来自不同来源的对象。这些对象不是您的模型 (POCO) 对象。您需要将这些对象转换为您的模型 (POCO) 对象。转换主要是复制操作。如果它是内部源,您可以直接从源填充这些 POCO 对象,但如果它是外部源,则不建议使用。外部源具有 API 以及它们使用的 Schema 的描述。然后在 DTO 中加载请求数据并在 POCO 中转换这些请求数据要容易得多。是的,这是一个额外的步骤,但有一个原因。规则是将源中的数据加载到对象中。它可以是 JSON、XML 等等。加载后,将该数据转换为模型中所需的数据。所以大多数时候 DTO 是外部源的对象图像。

于 2017-11-27T14:13:12.907 回答
-1

这是一般规则:DTO ==邪恶和过度设计软件的指标。POCO ==好。“企业”模式已经摧毁了 Java EE 世界中很多人的大脑。请不要在 .NET 领域重蹈覆辙。

于 2009-04-07T13:16:28.897 回答
-14

甚至不要称他们为 DTO。他们被称为模型......时期。模型永远不会有行为。我不知道是谁提出了这个愚蠢的术语 DTO,但它一定是一个 .NET 的东西是我能想到的。想想 MVC 中的视图模型,同样的 dam** 事物,模型用于在服务器端或网络期间在层之间传输状态,它们都是模型。带有数据的属性。这些是您通过电线传递的模型。模型,模型模型。就是这样。

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

于 2015-03-05T06:07:23.470 回答