POCO = 普通旧 CLR(或更好:类)对象
DTO = 数据传输对象
在这篇文章中有所不同,但坦率地说,我阅读的大多数博客都以定义 DTO 的方式描述 POCO:DTO 是用于在应用程序的层之间移动数据的简单数据容器。
POCO 和 DTO 是一回事吗?
POCO 遵循 OOP 的规则。它应该(但不是必须)具有状态和行为。POCO 来自 POJO,由 Martin Fowler [这里的轶事] 创造。他使用 POJO 一词作为一种方式,使拒绝框架重 EJB 实现更加性感。POCO 应该在 .Net 中的相同上下文中使用。不要让框架支配你的对象的设计。
DTO 的唯一目的是转移状态,并且不应该有任何行为。有关使用此模式的示例,请参阅 Martin Fowler对 DTO 的解释。
区别在于:POCO 描述了一种编程方法(老式的面向对象编程),其中DTO 是一种用于使用对象“传输数据”的模式。
虽然您可以将 POCO 视为 DTO,但如果这样做,您将面临创建贫血域模型的风险。此外,结构不匹配,因为 DTO 应该设计为传输数据,而不是代表业务领域的真实结构。这样做的结果是 DTO 往往比您的实际域更平坦。
在任何合理复杂的域中,您几乎总是最好创建单独的域 POCO 并将它们转换为 DTO。DDD(领域驱动设计)定义了反腐败层(这里的另一个链接,但最好的办法是买书),这是一个很好的结构,可以使隔离清晰。
因为我已经在我的博客文章中表明了我的立场,所以我贡献可能是多余的,但那篇文章的最后一段总结了一些事情:
因此,总而言之,要学会爱上 POCO,并确保您不会传播任何关于它与 DTO 相同的错误信息。DTO 是简单的数据容器,用于在应用程序的各层之间移动数据。POCO 是成熟的业务对象,唯一的要求是它们是 Persistence Ignorant(没有获取或保存方法)。最后,如果您还没有查看 Jimmy Nilsson 的书,请从您当地的大学书库中挑选。它有 C# 中的示例,非常适合阅读。
顺便说一句,Patrick,我将 POCO 作为生活方式文章阅读,我完全同意,这是一篇很棒的文章。这实际上是我推荐的 Jimmy Nilsson 书中的一部分。我不知道它可以在线获得。他的书确实是我在 POCO / DTO / Repository / 和其他 DDD 开发实践中找到的最佳信息来源。
POCO 只是一个不依赖外部框架的对象。这是平原。
POCO 是否有行为无关紧要。
DTO 可能是 POCO,也可能是域对象(通常具有丰富的行为)。
通常 DTO 更有可能依赖外部框架(例如属性)来进行序列化,因为它们通常在系统边界处退出。
在典型的 Onion 风格架构(通常在广泛的 DDD 方法中使用)中,领域层位于中心,因此其对象此时不应具有该层之外的依赖关系。
我认为 DTO 可以是 POCO。DTO 更多的是关于对象的使用,而 POCO 更多的是对象的样式(与架构概念解耦)。
POCO 与 DTO 不同的一个示例是,当您在域模型/业务逻辑模型中谈论 POCO 时,它是问题域的一种很好的 OO 表示。您可以在整个应用程序中使用 POCO,但这可能会产生一些不良副作用,例如知识泄漏。例如,DTO 用于 UI 与之通信的服务层,DTO 是数据的平面表示,仅用于向 UI 提供数据,并将更改传达回服务层。服务层负责将 DTO 的双向映射到 POCO 域对象。
更新Martin Fowler表示,这种方法是一条繁重的道路,只有在领域层和用户界面之间存在严重不匹配时才应采用。
DTO 描述了状态转移的模式。POCO 并没有描述任何东西,只是它没有什么特别之处。这是 OOP 中“对象”的另一种说法。它来自 POJO (Java),由 Martin Fowler 创造,他实际上只是将其描述为“对象”的一个更高级的名称,因为“对象”不是很性感,人们一直在避免它。
好的,以我认为需要的更高级的方式来解释这一点,从您最初关于 DTO 的问题开始:
DTO 是一种对象模式,用于在关注层之间传输状态。他们可以有行为(即技术上可以是 poco),只要该行为不会改变状态。例如,它可能有一个序列化自身的方法。
POCO 是一个普通的对象,但“普通”的意思是它不是特殊的,没有任何特定的要求或约定。它只是意味着它是一个没有隐含模式的 CLR 对象。一个通用术语。我还听说它扩展为描述它也不能与其他框架一起使用的事实。因此,例如,如果您的 POCO[JsonProperty]
在其属性中具有或 EF 装饰,那么我认为它不是 POCO。POCO 像您在学校学习创建的对象一样免费且不受阻碍
这里有一些不同类型的对象模式示例进行比较:
这些可以被视为只是对象,但请注意,它们中的大多数通常都与模式相关联或具有隐含的限制。所以你可以称它们为“对象”,或者你可以更具体地了解它的意图并用它来称呼它。这也是我们有设计模式的原因;用几句话来描述复杂的概念。DTO 是一种模式。聚合根是一种模式,视图模型是一种模式(例如 MVC 和 MVVM)。
POCO 没有描述模式。这只是在 OOP 中引用类/对象的一种不同方式,可以是任何东西。将其视为一个抽象概念;他们可以指任何东西。IMO,这是一种单向的关系,因为一旦一个对象到达它只能干净地服务于一个目的的地步,它就不再是一个 POCO。例如,一旦你用装饰标记你的类以使其与某些框架一起工作(即“插入”它),它就不再是 POCO。因此,我认为有一些逻辑关系,如:
区分两者的关键在于保持模式清晰和一致,以避免交叉关注点并导致紧密耦合。例如,如果您有一个业务对象,它具有改变状态的方法,但也使用 EF 装饰来装饰到地狱,以便保存到 SQL Server 和 JsonProperty,以便可以通过 API 端点将其发送回。该对象将无法容忍更改,并且可能会充斥着各种属性(例如,UserId、UserPk、UserKey、UserGuid,其中一些被标记为不保存到数据库中,而另一些则被标记为不被序列化为API 端点上的 JSON)。
因此,如果您要告诉我某些东西是 DTO,那么我可能会确保它除了移动状态之外从未用于任何其他用途。如果您告诉我某些东西是视图模型,那么我可能会确保它没有被保存到数据库中。如果你告诉我某个东西是域模型,那么我可能会确保它不依赖于域之外的任何东西。但是,如果您告诉我某些东西是 POCO,那么您实际上不会告诉我太多,除了它不是也不应该被检测。
从 Fowler 的解释中转述:在一个对象很花哨的世界中(例如,遵循特定的模式,有仪器等),它以某种方式鼓励人们避免使用不花哨的对象来捕获业务逻辑。所以他们给它起了一个花哨的名字POJO。如果您想要一个示例,他所指的是“实体 Bean”,它是具有非常具体的约定和要求等的对象之一。如果您不知道那是什么--> Java Beans .
相比之下,POJO/POCO 只是您在学校学习创建的常规 ole 对象。
DTO 的主要用例是从 Web 服务返回数据。在这种情况下,POCO 和 DTO 是等价的。POCO 中的任何行为在从 Web 服务返回时都会被删除,因此它是否具有行为并不重要。
DTO 对象用于将数据反序列化为来自不同来源的对象。这些对象不是您的模型 (POCO) 对象。您需要将这些对象转换为您的模型 (POCO) 对象。转换主要是复制操作。如果它是内部源,您可以直接从源填充这些 POCO 对象,但如果它是外部源,则不建议使用。外部源具有 API 以及它们使用的 Schema 的描述。然后在 DTO 中加载请求数据并在 POCO 中转换这些请求数据要容易得多。是的,这是一个额外的步骤,但有一个原因。规则是将源中的数据加载到对象中。它可以是 JSON、XML 等等。加载后,将该数据转换为模型中所需的数据。所以大多数时候 DTO 是外部源的对象图像。
这是一般规则:DTO ==邪恶和过度设计软件的指标。POCO ==好。“企业”模式已经摧毁了 Java EE 世界中很多人的大脑。请不要在 .NET 领域重蹈覆辙。
甚至不要称他们为 DTO。他们被称为模型......时期。模型永远不会有行为。我不知道是谁提出了这个愚蠢的术语 DTO,但它一定是一个 .NET 的东西是我能想到的。想想 MVC 中的视图模型,同样的 dam** 事物,模型用于在服务器端或网络期间在层之间传输状态,它们都是模型。带有数据的属性。这些是您通过电线传递的模型。模型,模型模型。就是这样。
我希望愚蠢的术语 DTO 能从我们的词汇中消失。