您好我正在开发一个需要使用Hibernate处理复杂域模型的应用程序。这个应用程序使用Spring MVC并且在表示层中使用域对象非常混乱,所以我认为我应该使用DTO来往于我的服务层,以便这些匹配我在视图中所需的内容。现在假设我有一个CarLease实体,其属性不是简单的java原语,但它与Make,Model等其他实体组成
public class CarLease {
private Make make;
Private Model model;
.
.
.
}
大多数属性都是这种方式,并且可以使用jsp视图上的下拉选项来选择它们,每个属性都会将ID发回控制器。
现在考虑一些标准用例:创建,编辑,显示
你将如何建模演示文稿DTO用作表单支持对象以及表示层和服务层之间的通信?
您是否会为每种情况(创建,编辑,显示)创建不同的DTO,您是否会针对复杂属性制作DTO?如果是这样,您将ID转换为实体?
您将如何以及在何处处理验证,DTO /域组装,您将从服务层方法返回什么? (创建,编辑,获取)
正如你所看到的,我现在将把我的视图与域对象分开(非常复杂,有很多我不需要的东西)。但是我很难找到任何真实的例子和最佳实践为了这。我需要从上到下的一些架构指导,请记住,我将使用Spring MVC,以防可能利用你的anwser。
提前感谢。
答案 0 :(得分:3)
对于它的价值(我正在使用C#.net进行开发 - 但原则应该仍然对您有所帮助)我定义了一堆类型(DTO),我用它来交换业务和数据之间的数据层;每个域对象/实体我有多个类型。这些被定义为简单类。
每个都是在考虑特定任务的情况下构建的,例如:
每种类型仅用于保存与给定任务相关的信息。 例如,“big”将返回上次修改的日期,但我不希望在我的保存和更新类型中,因为我填充了数据访问层中的那些。
此外,对于我的应用程序,这些类型存在于一个通用程序集中 - 因此它们可以在任何层之间重用,而不仅仅是在业务层和数据层之间。
建筑设计
这种方法没有什么特别之处,它有自己的优点和缺点;究竟是什么以及它们如何影响你将取决于很多事情 - 我猜你的里程会有所不同 - 但它确实很好地服务了我好几年了。
人们经常对“关注点分离”大惊小怪 - 这是一个非常明智的举动;这与DTO的关系在于它们在层(以及服务,组件等)之间进行交换,因此在准确绘制线的位置上总会存在一些模糊性。
我采取的方法是,如果一些信息适合在层之间交换,那么它可能适合在任意数量的层之间进行交换 - 那么为什么不让所有人都可以访问它?如果您只是通过它,它还可以节省重新投射信息。
就复杂性而言 - 有两种处理方式:
您是否会创建主要实体的复杂属性的DTO?
我发现自己制作DTO是出于以下两个原因之一:
因为它们都是在一个通用程序集中定义的,没有一个组件“拥有”它,它有助于你从“域”角度思考而不是以组件为中心思考;在某种程度上,这将影响DTO的设计(平衡重用与SRP)。
在这两种情况下,制作的DTO都可以根据特定需要安静,或者通用;例如,只有一个int和一个字符串的DTO并不罕见,这是你用来发送到下拉列表的那种东西。
我发回的大多数DTO集合(从DAL到BL)都是特定于概念的 - 而不是通用的。我通过我提供的构造函数对这些实施非常非常基本的规则:每个arg都是必需的。我不确定这是否回答了您的问题“如何管理装配和验证”。
答案 1 :(得分:1)
服务层应该返回DTO而不是EJB对象的想法主要是EJB3 / JPA之前的想法。在CRUD期间,您通过直接使用模型对象(a.k.a. entities)获得了很多。
然而,当用于性能优化时,您可以从使用DTO中受益,例如,当模型对象过于庞大或者使用某些智能连接来聚合模型数据时,您将获益。
因此,除非您在SOA下进行工程设计,否则我不建议您使用DTO进行CRUD操作。
答案 2 :(得分:1)
您是否考虑过命令查询责任分离(CQRS)原则?简而言之,它是一种架构原则,主张使用单独的组件进行读取和修改操作。
使用发送到域模型的命令完成修改。您的NewCarLeaseDTO看起来像一个命令 - CreateNewCarLeaseCommand。它们包含特定操作所需的所有数据。
另一方面,读取(列表或详细信息)直接在底层数据存储(SQL数据库)上完成。这里有很多可能,从拥有相同的数据存储支持读写部分到通过发布/订阅基础设施连接两个独立的数据存储。
当使用后者(两个商店)的方法来阅读时,很多人(比如Udi Dahan)主张使用所谓的持久性观点。这意味着直接以您的视图可以使用的形式存储数据。在模型更新后同步时完成转换(非规范化)。
如果您想了解更多关于CQRS的信息,我建议您阅读Udi Dahan和Greg Young。