DTO代替实体有什么用?


18

我正在研究RCP应用程序,我是这个应用程序的新手。

Spring bean用于编写业务逻辑来保存/获取实体。

但是,我们不是将实体直接发送给客户端,而是转换为DTO并填充客户端。保存时,我们再次将DTO转换为实体并保存。

这些转换有什么好处?有人可以解释吗?


What's the benefit of these conversions?将持久性数据模型与提供给使用者的数据模型(表示)分离。去耦的好处已在SE中进行了广泛的讨论。但是,DTO的目的是在单个响应中收集尽可能多的客户端保存到服务器的呼叫所需的信息。是什么使通讯客户端-服务器更流畅。
Laiv


您的例子很好。当您是客户时(视图...)很难更改,但是最大的问题是当系统已经具有第三者的集成时,就不可能更改(合同,费用...)。如果您的系统将具有第三方集成,请使用DTO。
卢卡斯·贡萨尔维斯

Answers:


45

每当开发人员问“这样做的目的是什么?”时,他们真正的意思是“我认为这样做没有好处的用例”。为此,让我向您展示一些示例。


所有示例都将基于以下简单数据模型:

一个Person实体有五个属性:Id, FirstName, LastName, Age, CityId

您可以假设应用程序以多种方式使用此数据(报告,表单,弹出窗口等)。

整个应用程序已经存在。我提到的所有内容都是对现有代码库的更改。要记住这一点很重要。


示例1-更改基础数据结构-不使用DTO

要求已更改。需要从政府的数据库中动态检索此人的年龄(假设基于他们的姓氏和名字)。

由于您不再需要在Age本地存储值,因此需要将其从Person实体中删除。在这里重要的是要认识到实体代表数据库数据,仅此而已。如果不在数据库中,则不在实体中。
当您从政府的Web服务中检索年龄时,该年龄将存储在其他对象(或int)中。

但是您的前端仍然显示年龄。已设置所有视图以使用该Person.Age属性,该属性现在不再存在。存在一个问题:必须解决所有引用Age一个人的观点


示例2-更改基础数据结构-使用DTO

在旧系统中,还存在PersonDTO具有相同五个属性的实体:Id, FirstName, LastName, Age, CityId。检索a之后Person,服务层会将其转换为a PersonDTO,然后将其返回。

但是现在,要求已更改。需要从政府的数据库中动态检索此人的年龄(假设基于他们的姓氏和名字)。

由于您不再需要在Age本地存储值,因此需要将其从Person实体中删除。在这里重要的是要认识到实体代表数据库数据,仅此而已。如果不在数据库中,则不在实体中。

但是,由于您有中介人PersonDTO,因此请务必注意此类可以保留Age属性。服务层将获取Person,将其转换为PersonDTO,然后还将从政府的Web服务获取该人的年龄,将该值存储在中PersonDTO.Age,然后传递该对象。

这里的重要部分是,使用服务层的任何人都不会看到新旧系统之间的区别。这包括您的前端。在旧系统中,它收到了一个完整的PersonDTO对象。并且在新系统中,它仍然接收完整的PersonDTO对象。视图不需要更新

这就是我们使用关注点分离的意思:有两个不同的关注点(将数据存储在数据库中,将数据呈现给前端),并且它们各自需要不同的数据类型。即使这两种数据类型现在恰好包含相同的数据,将来也可能会改变。
在给定的示例中,这Age是两种数据类型之间的区别:(Person数据库实体)不需要Age,但是PersonDTO(前端数据类型)需要。
通过从一开始就将关注点分离(=创建单独的数据类型),代码库就可以更灵活地适应对数据模型所做的更改。

您可能会争辩说,将DTO对象添加到数据库中时,这意味着您必须做双重工作,在实体和DTO中都添加属性。从技术上讲这是正确的。维护两个类而不是一个类需要一些额外的工作。

但是,您需要比较所需的工作量。当添加一个或多个新列时,复制/粘贴一些属性不会花费很长时间。当数据模型发生结构性更改时,必须以可能仅在运行时(而不是在编译时)引起错误的方式更改前端,这会花费更多的精力,并且需要开发人员去寻找错误。


我可以给您更多示例,但原理将始终相同。

总结一下

  • 单独的职责(问题)需要彼此分开工作。他们不应共享任何资源,例如数据类(例如Person
  • 仅仅因为一个实体及其DTO具有相同的属性,并不意味着您需要将它们合并到同一实体中。不要偷工减料。
    • 举一个更加公开的例子,我们的数据库包含国家,歌曲和人物。所有这些实体都有一个Name。但是,仅仅因为它们都具有Name属性,并不意味着我们应该使它们从共享的EntityWithName基类继承。不同的Name属性没有有意义的关系。
    • 如果其中一个属性发生了变化(例如,一首歌Name被重命名为Title,或者一个人获得了FirstNameLastName),则您将不得不花费更多的精力来撤销一开始就不需要的继承。
    • 尽管不那么公道,但是关于拥有实体时不需要DTO的论点是相同的。您正在查看now,但没有为将来的任何更改做准备。IF实体和DTO是完全一样的,如果你能保证绝不会有数据模型的任何变化; 那么您可以省略DTO是正确的。但事实是,您永远不能保证数据模型永远不会改变。
  • 好的做法并不总是能立即得到回报。当您需要重新访问旧的应用程序时,它可能会在将来获得回报。
  • 现有代码库的主要杀手是让代码质量下降,从而使其维护起来更加困难,直到它演变成无用的无法维护的意大利面条代码。
  • 良好实践(例如,实现关注点与目标的分离)的目的在于避免不好的维护工作,以使代码库可维护的时间尽可能长。

根据经验,考虑将关注点分离,请按以下方式考虑:

假设每个问题(UI,数据库,逻辑)均由位于不同位置的不同人员处理。他们只能通过电子邮件交流。

在分隔良好的代码库中,对特定问题的更改仅需要一个人处理:

  • 更改用户界面仅涉及UI开发人员。
  • 更改数据存储方法仅涉及数据库开发人员。
  • 更改业务逻辑仅涉及业务开发人员。

如果所有这些开发人员都使用同一Person实体,并且对该实体进行了较小的更改,则每个人都需要参与该过程。

但是通过为每一层使用单独的数据类,该问题并不那么普遍:

  • 只要数据库开发人员可以返回有效PersonDTO对象,业务和UI开发人员就不必关心他是否更改了存储/检索数据的方式。
  • 只要业务开发人员将数据存储在数据库中,并将所需的数据提供给前端,数据库和UI开发人员就不会在乎他是否决定重做业务规则。
  • 只要可以基于`PersonViewModel设计UI,则UI开发人员可以根据需要构建UI。数据库和业务开发人员并不关心它的完成方式,因为它不会影响它们。

这里的关键词是因为它不影响他们。实现关注点的良好分离旨在最大程度地减少对其他方的影响(并因此必须使其参与)。

当然,某些重大更改无法避免包含多个人,例如,当将一个全新的实体添加到数据库时。但是,请不要低估在应用程序生存期内必须进行的微小更改的数量。重大变化是少数。


综合答案,谢谢。我有问题;如果您回答以下问题,请赞赏:1-如果我错了,请纠正我。业务对象视图对象用于在表示层业务层之间传输数据,实体对象用于在业务层数据访问层之间传输数据。DTO可以用作哑BO。2-假设两个视图需要一个公司的不同信息,那么我们需要两个不同的companyDTO?
Arash

1
@Arash(1)“ DTO”实际上是用于在两层之间进行交换的任何数据类的全面定义。业务对象和视图对象都是DTO。(2)这在很大程度上取决于很多事情。为您需要的每个字段集合创建一个新的dto是一项繁琐的任务。简单地返回一个完整的公司DTO(在合理的情况下),然后让视图选择它感兴趣的领域,并没有什么天生的错误。这是在实现适当的关注点分离与避免过度设计和毫无意义的重复之间找到平衡的问题。
平坦

现在这对我来说很有意义。非常感谢。平一点
Arash

还有一个问题。您说的是“业务对象视图对象”。我认为两者是平等的。搜索时,我意识到与Object相比,Business Object具有一般意义。但是业务对象应该从用例派生而实体对象应该从数据模型派生,因此它们是不同的,对吗?你能解释一下吗?
Arash

@Arash:所谓的“业务对象”和“视图对象”之间的区别是上下文。对于我们人类而言,这种区别对于正确理解事物至关重要。但是编译器(以及扩展语言本身)看不到它们之间的技术差异。当我说它们相同时,我的意思是从技术角度来看。两者都是具有用于保存数据并传递的属性的类。在这方面,它们之间没有区别。
扁平的
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.