- 已关闭**。此问题为opinion-based。当前不接受答案。
- 想要改进此问题吗?**请更新此问题,以便editing this post可以用事实和引文来回答。
六年前关闭了。
Improve this question
在阅读了Stackoverflow上的一些Q/A之后,我仍然对在我的Web应用程序中正确实现DTO感到困惑。我当前的实现是一个(基于Java EE的)多层体系结构(具有持久层、服务层和表示层),但所有层都使用一个"公共"包,其中包含域对象。在这种情况下,这些层不能真正被视为独立的。
在重新设计中,我计划一步一步地删除"通用"包。
问题
在重新设计期间,我遇到了各种挑战/问题:
1.假设持久层将使用类 * myproject. persistence. domain. UserEntity *(基于JPA的实体)来存储数据并从数据库加载数据。为了在视图中显示数据,我将提供另一个类 * myproject. service. domain. User 。我在哪里转换它们?用户的服务是否负责在两个类之间转换?这真的有助于改善耦合吗?
1. User * 类应该是什么样子的?它应该只包含getter才是不可变的吗?视图编辑现有用户(创建一个新的 * User ,使用现有 * User * 对象的getter等)不是很麻烦吗?
1.我应该使用相同的DTO类( User *)向服务发送修改现有用户/创建新用户的请求,还是应该实现其他类?
1.通过使用 * myproject.service.domain * 中的所有DTO,表示层是否会非常依赖于服务层?
1.如何处理我自己的异常?我目前的方法会重新抛出大多数"严重"异常,直到它们被表示层处理(通常它们会被记录下来,并通知用户出了问题)。一方面,我有一个问题,我又有一个共享包。另一方面,我仍然不确定这是否可以被认为是"最佳实践"。有什么想法吗?
2条答案
按热度按时间mwg9r5ms1#
在不同的层中有一些包并不罕见,但是通常只在横切的情况下才这样做,比如日志记录。你的模型不应该被不同的层共享,否则对模型的更改将需要在所有这些层中进行更改。通常,你的模型是一个较低的层,靠近数据层(在数据层之上、之下或交织在一起,取决于方法)。
顾名思义,数据传输对象是用于传输数据的简单类。因此,它们通常用于层之间的通信,特别是在SOA体系结构通过消息而不是对象进行通信时。DTO应该是不可变的,因为它们的存在只是为了传输信息,而不是更改信息。
域对象是一回事,DTO是另一回事,表示层中所需的对象又是另一回事。然而,在小型项目中,实现所有这些不同的集合并在它们之间进行转换可能不值得。这取决于您的需求。
您正在设计一个Web应用程序,但问问自己“我可以将Web应用程序切换为桌面应用程序吗?我的服务层真的不知道我的表示逻辑吗?"可能会对您的设计有所帮助。用这些术语思考将引导您走向更好的体系结构。
回答您的问题
1.假设持久层将使用类myproject.persistence.domain.UserEntity(一个基于JPA的实体)来存储数据和从数据库加载数据。为了在视图中显示数据,我将提供另一个类myproject. service. domain. User。我在哪里转换它们?用户服务是否负责在两个类之间转换?这真的有助于改善耦合吗?
服务层知道它的类(DTO)和它下面的层(比如说持久性),所以是的,服务负责持久性和它自己之间的转换。
DTO背后的思想是,您只使用它们进行传输,因此不需要创建新用户之类的操作。为此,您需要不同的对象。
1.我应该使用相同的DTO类(用户)向服务发送修改现有用户/创建新用户的请求,还是应该实现其他类?
服务方法可以表示操作,DTO是其仅包含数据的参数。另一种选择是使用表示操作且还包含DTO的命令。这在SOA体系结构中很流行,在SOA体系结构中,服务可能只是一个命令处理器,例如具有一个将
ICommand
接口作为参数的单个Execute
操作(与每个命令具有一个操作相反)。1.通过使用myproject.service.domain中的所有DTO,表示层是否会非常依赖于服务层?
是的,服务层之上的层将依赖于它。这就是这个想法。好处是只有那个层依赖于它,没有上层或下层,所以改变只影响那个层(不像如果你使用每个层的域类会发生什么)。
1.如何处理我自己的异常?我目前的方法会重新抛出大多数“严重”异常,直到它们被表示层处理(通常它们会被记录下来,并通知用户出了问题)。一方面,我有一个问题,我又有一个共享包。另一方面,我仍然不确定这是否可以被认为是“最佳实践”。有什么想法吗?
每一层都有自己的异常。它们从一层流向另一层,封装成下一种异常。有时,它们将由一层处理,该层将执行某些操作(例如,日志记录),然后可能抛出上层必须处理的不同异常。它们可能会被处理,问题可能会被解决。2例如,连接到数据库时出现问题,它会抛出一个异常。你可以处理它,并决定在一秒钟后重试,然后可能会成功,因此异常不会向上流动。如果重试也失败了,异常将被重新抛出,它可能会一直流动到表示层,在那里你优雅地通知用户,并要求他重试层。
34gzjxbg2#
松耦合确实是推荐的方法,这意味着您最终会在业务逻辑中遇到庞大、编写起来很无聊、维护转换器很痛苦的问题。是的,它们属于业务逻辑:DAO和视图之间的层。所以业务层最终将依赖于DAO DTO和视图DTO。并且将充满Converter类,稀释了实际业务逻辑的视图...
如果您可以不使用不可变视图DTO,那就太好了。不过,您用来序列化它们的库可能需要它们具有setter。或者,如果它们具有setter,您可能会发现它们更容易构建。
对于视图和DAO使用相同的DTO类,我已经过得很好了。这很糟糕,但老实说,我没有感觉到系统在其他方面更加解耦,因为业务逻辑,最基本的部分,无论如何都必须依赖于一切。这种紧密耦合提供了很大的简洁性,并且使视图和DAO层的同步变得更容易。我仍然可以通过使用复合来使特定于其中一个层的东西在另一个层中看不到。
最后,关于异常,这是最外层视图层的职责(如果你使用Spring的话是控制器)来捕捉从内层传播的错误,无论是使用异常,还是使用特殊的DTO字段。然后这个最外层需要决定是否通知客户端错误,以及如何通知。事实是,一直到最内层,你需要区分最外层需要处理的不同类型的错误。2例如,如果在DAO层发生了一些事情,视图层需要知道是返回400还是500,DAO层将需要向视图层提供决定使用哪一个所需的信息,并且该信息将需要通过所有中间层,他们应该能够添加自己的错误和错误类型。将IOException或SQLException传播到最外层是不够的,内层还需要告诉外层这是否是预期的错误。可悲但真实。