视图层中的DTO或域模型对象?

时间:2010-04-21 03:14:10

标签: model dto data-transfer-objects

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

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

我应该注意我的应用程序使用Hibernate模型对象并使用自己的自定义创建的模型对象(意味着没有绑定到任何数据库会话,总是分离)。以上任一方案对严格的模型对象模式更有利吗?对于像Lazy Initialization Exceptions这样的事情,使用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。在那种情况下你会做什么?检索完整的教师信息和学生关系,然后您的代码计入学生列表,然后计算内部所有gpas的总平均值?这似乎比简单地创建一个带有'String lastName','int numStudents'和'double combinedGpa;

的DTO更加努力

听起来我的想法可能就是这些,但我还没有在一个应用程序中工作,在这个应用程序中,模型对象可以在每个实例中完全使用。具有不同寻常的用户需求的常规实际应用程序不能以这种方式工作。

9 个答案:

答案 0 :(得分:33)

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

  1. 您很想修改域对象以适应视图层中所需的内容
  2. 您的视图层将包含由域对象提供的内容与您的视图真正需要的内容不匹配所导致的额外复杂性。您可能无法绕过这种复杂性,但它可能不属于View层。
  3. 如果您的域对象很简单并且您的视图很少,那么跳过DTO可能是最简单的事情。

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

答案 1 :(得分:8)

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

我认为DTO仅在需要序列化对象时才有用。如果没有通过电线或不兼容的架构进行传输,我就不会使用它们。 DTO模式对于保持域对象的序列化非常有用。考虑到UI / Domain交互不需要序列化,请保持简单并使用实际对象。

答案 2 :(得分:7)

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

我已经使用只包含域对象并将大部分操作委托给它们的视图模型获得了很好的体验。这使视图和域层分离,允许灵活组合域对象,但仍然没有太多工作要实现因为IDE支持委托模式。

答案 3 :(得分:2)

发送域对象时出现问题的场景:

  1. 您可能需要汇总信息或发送到UI层的其他类型的“计算字段”(例如Flex / GWT)并且不想弄乱域对象
  2. 您可能会遇到序列化循环对象图的需要(在您的示例中,如果Student有List关系),某些协议存在问题
  3. 处理框架序列化程序时的Hibernate LazyInitializationException(blazeDS for flex / GWT序列化程序)
  4. 我不确定在这些情况下这是一个明确的答案

答案 4 :(得分:2)

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

此外,对于每个DTO,你正在创建一个。)你现在必须维护的额外类,并且b。)在某个类中的某个地方至少有一个额外的方法来构造和填充DTO(DAO,工厂方法,等等。)。更多维护= 6个月后不开心的开发人员。

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

答案 5 :(得分:0)

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

域名必须封装,并且不受与域名健康状况无关的人的修改。

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

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

答案 6 :(得分:0)

使用DTO的原因之一是当您需要向不同类型的用户显示信息时。例如。您有一个“帐户”域模型,并且具有某些属性,例如“ created_at”。根据您的业务逻辑,您可以向管理员和用户显示帐户,但是实际上不允许用户知道帐户的创建日期(出于某些公司的秘密原因)。向管理员和用户提供域模型可能非常危险,尤其是在使用JSON API的情况下,但是如果将表示形式分为2个不同的DTO(对于管理员和用户),则将更加安全。我不得不说,这将需要更多的时间来处理和维护此问题,但是如果您的应用需要这种类型的安全性和严格性,那么您别无选择。

答案 7 :(得分:-1)

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

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

最后,我个人认为域对象应该放到视图本身。想象一下wicket集成是什么样的。但是以Spring MVC为例 - 域可能会留在应用程序层中......

答案 8 :(得分:-4)

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

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