51

我知道这可能是一个古老的问题,但更好的做法是什么?在应用程序的所有层中使用域模型对象,甚至在 JSP 上将值直接绑定到它们(我使用的是 JSF)。或者在 DAO 或 Service 层将领域模型对象转换为 DTO,并将轻量级 DTO 发送到表示层。

我被告知使用 DTO 没有任何意义,因为对数据库的更改将导致对所有 DTO 的更改,而在任何地方使用模型对象只需要对受影响的模型对象进行更改。然而,DTO 的易用性和轻量级特性似乎超过了这一点。

我应该注意到我的应用程序使用 Hibernate 模型对象并使用它自己的自定义创建的模型对象(意味着不绑定到任何数据库会话,始终分离)。上述任何一种情况对严格的模型对象模式更有利吗?就延迟初始化异常之类的事情而言,使用 Hibernate 是一个巨大的 PITA。

我正在编辑这个问题,希望能进一步讨论(不确定我是否做得对):

我对模型对象的问题是它们根本不灵活。下面的评论说应用程序的设计应该使模型对象可以在所有层中使用。为什么?如果用户想要一个荒谬的功能,我是否应该告诉他们,'这不适用于模型对象'?

简单明了,有时模型对象不起作用。你可能有:

public class Teacher {
    List<Student> students;
    [tons of other Teacher-related fields]
}
public class Student {
    double gpa;
   [tons of other Student-related fields]
}

但也许您不需要所有这些信息。你只需要老师的姓氏,他们今年教的学生人数,以及所有学生的平均 GPA 加起来。在那种情况下你会怎么做?检索完整的教师信息和学生关系,然后您的代码计算学生列表,然后计算其中所有 gpa 的总平均值?这似乎比简单地使用“String lastName”、“int numStudents”和“double combineGpa;”创建一个 DTO 更加努力;

这听起来像是我已经下定决心了,但我还没有在一个应用程序中工作,其中模型对象可以在每个实例中完全干净地使用。具有非同寻常的用户需求的常规现实世界应用程序无法以这种方式工作。

4

9 回答 9

37

这实际上取决于您的应用程序的复杂性。将领域对象混合到视图层有两个可能的含义:

  1. 你会很想修改你的域对象以适应你在视图层中需要的东西
  2. 您的视图层将包含额外的复杂性,这是由于您的域对象提供的内容与您的视图真正需要的内容不匹配而导致的。您可能无法绕过这种复杂性,但它可能不属于 View 层。

如果您的领域对象很简单并且您的视图很少,那么跳过 DTO 可能是最简单的事情。

另一方面,如果您的领域模型可能会发展并变得复杂,并且如果您的视图可能是多种多样的,那么拥有视图特定对象可能是一个好主意。在 MVC 世界中,使用 ViewModel 很常见,对我来说很有意义。

于 2011-09-27T15:40:59.003 回答
8

对域对象的另一票。就领域驱动设计而言,领域模型为王,应尽可能使用。应用程序应该以大多数层(bar Infrastructure 层)可以使用域对象的方式设计。

我认为 DTO 仅在需要序列化对象的情况下才有用。如果没有通过线路传输或进入不兼容的架构,我将不会使用它们。DTO 模式对于将序列化排除在域对象之外很有用。考虑到 UI/Domain 交互不需要序列化,保持简单并使用实际对象。

于 2010-04-21T03:32:25.497 回答
7

我认为拥有 DTO 通常不是一种反模式。有很多人和系统在使用它们,您获得的好处是可以独立于域模型进行设计和模块化的解耦视图层。尽管我同意您应该尽可能使用域对象,但是当您将视图层直接绑定到域模型时,您可能会遇到问题。

我在仅包含域对象并将大部分操作委托给它们的视图模型方面取得了很好的经验。这将视图和域层解耦,允许域对象的灵活组合,并且由于 IDE 支持,实现起来仍然没有太多工作委托模式。

于 2011-12-20T17:56:07.570 回答
3

域对象发送有问题的场景:

  1. 您可能需要将聚合信息或其他类型的“计算字段”发送到 UI 层(例如 Flex / GWT),并且不想弄乱域对象
  2. 您可能会遇到需要序列化循环对象图(在您的示例中,如果 Student 有 List 关系),某些协议有问题
  3. Hibernate LazyInitializationException 处理框架序列化程序时( blazeDS 用于 flex / GWT 序列化程序)

我不确定在这些情况下这是一个明确的答案

于 2010-07-10T19:44:55.507 回答
2

在我看来,在每一层中使用域模型对象完全没有问题。你说你不需要所有的信息。当您在 JSP 中时,只使用您需要的数据。没有人强迫您获取所有财产。您还说过,您需要进行与对象属性相关的计算,以获得 GPA、学生人数等。您有 3 个选项:在您的域模型对象中创建合成属性,为您返回正确的数据,结束得很好整洁;在控制器或服务层中进行计算,并通过适当的 getter 公开它们;或者在你的 JSP 中处理它。无论如何,您都需要检索/编译/整理数据,那么为什么要使用 DTO 增加更多复杂性。

此外,对于每个 DTO,您正在创建 a.) 您现在必须维护的额外类,以及 b.) 在构建和填充 DTO(DAO、工厂方法等)的某个类中的某个地方至少有 1 个额外方法. 更多维护 = 6 个月后开发人员不满意。

所以,我反对 DTO。我确实使用它们,但仅在某些情况下使用,例如当我真的需要优化速度和/或内存使用时,并且水合完整域模型对象的成本太高了。Web 服务是我喜欢使用 DTO 的一个很好的例子。

于 2013-04-10T06:00:00.957 回答
0

类的行为或其内部方法不应暴露给与其行为无关的层。传输数据,而不是行为。使用域内的域对象。Web 不是一个受控域,UI 开发人员不需要关心域行为,只关心数据。

域必须被封装,并保护不被不关心域健康的人修改。

泄漏行为不是最好的习惯。

如果它是一个小项目,也要按照正确的原则构建它。这样,我们始终牢记我们为什么做我们所做的事情,而不仅仅是如何做。

于 2015-03-23T17:34:57.920 回答
0

One of the reasons to use DTOs is when you need to display information to different types of users. E.g. you have an "account" domain model, and it has some property, e.g. "created_at". Depending on your business logic, you could show account to admins and to users, but users are not really allowed to know about creation date of an account (for some company secret reasons). Serving domain model to both admin and users could be very dangerous, especially if you are using JSON API, but if you split representation into 2 different DTOs - for admins and users, then it will be much safer. I have to say that it will require much more time to deal with and maintain this, but if your app requires this type of security and strictness, then you have no other choice.

于 2020-09-14T02:38:41.360 回答
-1

我认为我们首先应该考虑的是引入新层的成本。以 DTO 为例 - 这样做我们需要一个映射。正如有人所说,翻译是邪恶的,应该尽可能避免。

另一方面,我认为您通常不应该做的事情很少。那些说所有 DTO 都是邪恶的人是错误的——它总是取决于用例!他们真的有道理吗?

最后,我个人认为应该将领域对象交给视图本身。想象一下检票口集成是什么样的。但是以 Spring MVC 为例 - 域可能会留在应用层中......

于 2013-08-18T07:37:40.407 回答
-4

如今,DTO 被广泛认为是一种反模式,建议通常是“不惜一切代价避免它们”。

像 Hibernate 这样的 ORM 框架的主要优点之一是您可以在所有级别使用域对象,并且不需要 DTO。当然,需要注意的是,您必须花一些时间来思考这些关系:何时使用惰性获取,何时使用渴望等等。

于 2010-04-21T03:22:10.313 回答