Mic*_*ows 558
POCO遵循OOP规则.它应该(但不一定)具有状态和行为.POCO来自POJO,由Martin Fowler创作[ 轶事在这里 ].他使用术语POJO作为一种方法,使其更容易拒绝框架繁重的EJB实现.POCO应该在.Net中使用相同的上下文.不要让框架决定你的对象的设计.
DTO的唯一目的是转移状态,而且应该没有行为.有关使用此模式的示例,请参阅Martin Fowler 对DTO的说明.
这就是区别:POCO描述了一种编程方法(良好的老式面向对象编程),其中DTO是一种用于使用对象"传输数据" 的模式.
虽然您可以像对待DTO一样对待POCO,但如果您这样做,则存在创建贫血域模型的风险.此外,结构不匹配,因为DTO应设计为传输数据,而不是代表业务域的真实结构.结果是DTO往往比您的实际域更平坦.
在任何合理复杂的领域,您几乎总是更好地创建单独的域POCO并将它们转换为DTO.DDD(域驱动设计)定义了反腐败层(这里的另一个链接,但最好的办法是购买书),这是一个很好的结构,使隔离清晰.
小智 49
由于我已经在我的博客文章中说明了我的立场,因此我可能有多余的贡献,但该文章的最后一段总结了一些事情:
因此,总而言之,学会爱POCO,并确保不传播任何与DTO相同的错误信息.DTO是用于在应用程序层之间移动数据的简单数据容器.POCO是完全成熟的业务对象,其中一个要求是Persistence Ignorant(无get或save方法).最后,如果你还没有查看Jimmy Nilsson的书,请从你当地的大学校园里拿出来.它有C#中的示例,这是一个很好的阅读.
顺便说一下,Patrick我把POCO看成是一篇生活方式文章,我完全同意,这是一篇很棒的文章.这实际上是我推荐的吉米尼尔森书中的一部分.我不知道它是否可以在线获取.他的书确实是我在POCO/DTO/Repository /和其他DDD开发实践中发现的最佳信息来源.
Nei*_*eil 28
POCO只是一个不依赖于外部框架的对象.这是普拉恩.
无论POCO是否有行为,它都是无关紧要的.
DTO可以是POCO,也可以是域对象(其通常具有丰富的行为).
通常,DTO更可能依赖于外部框架(例如,属性)以用于序列化目的,因为它们通常在系统的边界处退出.
在典型的洋葱风格体系结构中(通常在广泛的DDD方法中使用),域层位于中心,因此其对象在此时不应具有该层之外的依赖性.
Sin*_*tic 12
DTO 描述了状态传输的模式。POCO 没有描述太多东西,只是没有什么特别之处。这是 OOP 中“对象”的另一种表达方式。它来自 POJO (Java),由 Martin Fowler 创造,他实际上只是将其描述为“对象”的一个更奇特的名称,因为“对象”不是很性感,人们因此避免使用它。
好吧,以一种我认为需要的更为高调的方式来解释这一点,首先从您关于 DTO 的原始问题开始:
DTO 是一种对象模式,用于在关注层之间传输状态。它们可以有行为(即技术上可以是 poco),只要该行为不会改变状态。例如,它可能有一个将自身序列化的方法。要成为一个合适的 DTO,它需要是一个简单的属性包;需要明确的是,该对象不是一个强模型,它没有隐含的语义含义,并且它不强制执行任何形式的业务规则或不变量。它实际上只是为了移动数据而存在。
POCO 是一个普通对象,但“普通”的意思是它并不特殊,没有任何特定的要求或约定。它只是意味着它是一个没有隐含模式的 CLR 对象。通用术语。我还听说它被扩展来描述这样一个事实:它也不能与其他框架一起使用。因此,例如,如果您的 POCO 在其属性上有一堆 EF 装饰,那么我认为它不是一个简单的 POCO,它更多地属于 DAO 领域,我将其描述为以下内容的组合: DTO 和其他数据库问题(例如映射等)。POCO 是免费且不受阻碍的,就像您在学校学习创建的对象一样
下面是一些要比较的不同类型对象模式的示例:
这些可以被视为只是对象,但请注意,它们中的大多数通常与模式相关或具有隐含的限制。因此,您可以将它们称为“对象”,或者您可以更具体地说明其意图并按其本质来称呼它。这也是我们有设计模式的原因;用几句话描述复杂的概念。DTO 是一种模式。聚合根是一种模式,视图模型是一种模式(例如 MVC 和 MVVM)。
POCO 不描述模式。它只是 OOP 中引用类/对象的一种不同方式,可以是任何东西。将其视为一个抽象概念;他们可以指任何事物。在我看来,这是一种单向关系,因为一旦一个对象达到只能完全服务一个目的的地步,它就不再是 POCO。例如,一旦您用装饰标记您的类以使其与某些框架一起工作(即“检测”它),它就不再是 POCO。因此我认为存在一些逻辑关系,例如:
区分两者的要点是保持模式清晰和一致,以免交叉关注点并导致紧密耦合。例如,如果您有一个业务对象,它具有改变状态的方法,但也使用 EF 装饰来保存到 SQL Server 和 JsonProperty,以便可以通过 API 端点发送回。该对象无法容忍更改,并且可能会散布各种属性的变体(例如 UserId、UserPk、UserKey、UserGuid,其中一些标记为不保存到数据库,另一些标记为不序列化到数据库) API 端点处的 JSON)。
因此,如果您告诉我某个东西是 DTO,那么我可能会确保它除了移动状态之外从未用于任何其他用途。如果你告诉我某个东西是视图模型,那么我可能会确保它没有保存到数据库中,并且我知道可以将“hacky”东西放在那里以确保数据可用通过用户界面。如果你告诉我某个东西是领域模型,那么我可能会确保它不依赖于域之外的任何内容,当然也不依赖于任何技术实现细节(数据库、服务等),仅依赖于抽象。但是,如果你告诉我某个东西是 POCO,那么除了它不是也不应该被检测之外,你根本就不会告诉我太多。
这是一个弱但准确的示例,应该很容易理解。
这可以是 POCO,也可以是 DTO。它只是一个对象;没什么特别的。看起来像一个弱类型的属性包,但没有什么值得注意的地方。
public class CreateUserRequest
{
public string Name { get; set; }
public string Email { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
这不再是 POCO
public class CreateUserRequest
{
[JsonPropertyName(Name = "name")]
public string Name { get; set; }
[JsonPropertyName(Name = "email")]
public string Email { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
为什么 POCO 不再存在了?因为它显然是一个数据协定,旨在与 System.Text.Json 序列化程序一起使用。它现在更接近 DTO,但针对特定框架进行了检测。
下一个是当你不进行这些区分时会发生的情况
[Table("Users")]
public class CreateUserRequest
{
[Key]
[JsonIgnore]
public string Id { get; set; }
[JsonPropertyName(Name = "name")]
public string Name { get; set; }
[JsonPropertyName(Name = "email")]
public string Email { get; set; }
public int LoginCount { get; set; }
public void IncrementLogin() => LoginCount++;
}
Run Code Online (Sandbox Code Playgroud)
所以现在这绝对不再是 POCO 了。它看起来像是某种 DTO,但它的用途是超载的。是 API 合约还是 DAO?它似乎既可以作为 JSON 合约,也可以与数据库 ORM 一起使用。需要额外的工具来防止它从 REST API 中泄漏数据库 PK。它还具有一种改变状态的方法,就好像有人将其用作域实体一样。甚至不清楚开发人员是否打算将其LoginCount作为 JSON 合约或数据库架构的一部分。
我从开发人员那里看到很多这样的类,他们认为通过重用类可以节省时间。他们认为这是干的。我想你可能会说它是 DRY,但实际上,它是人为的、紧密耦合的,可能违反了其他 5 个设计理念,并且最终会在未来把你搞砸。
转述福勒的解释:在一个对象很奇特的世界中(例如遵循特定的模式,有仪器等),它在某种程度上鼓励人们避免使用不奇特的对象来捕获业务逻辑。所以他们给它起了一个好听的名字POJO。如果你想要一个例子,他指的是“Entity Bean”,它是具有非常具体的约定和要求等的对象之一。如果你不知道那是什么--> Java Beans。
相比之下,POJO/POCO 只是您在学校学习如何创建的常规 ole 对象。
我认为DTO可以是POCO.DTO更多地是关于对象的使用,而POCO更像是对象的风格(与架构概念分离).
POCO与DTO不同的一个例子是当你在域模型/业务逻辑模型中讨论POCO时,这是你问题域的一个很好的OO表示.您可以在整个应用程序中使用POCO,但这可能会产生一些不良副作用,例如知识泄漏.例如,DTO是从与UI通信的服务层使用的,DTO是数据的平面表示,并且仅用于向UI提供数据,并且将更改传送回服务层.服务层负责将DTO的两种方式映射到POCO域对象.
更新 Martin Fowler 表示,这种方法很难走,只有在域层和用户界面之间存在严重不匹配时才应采取这种方法.