在不同层之间具有某些包是很常见的,但通常只用于横切关注点,例如日志记录。您的模型不应由不同的层共享,否则对模型的更改将需要更改所有这些层。通常,您的模型是较低的层,靠近数据层(上面,下面或交织在一起,具体取决于方法)。
数据传输对象(DTO)如其名称所示,是用于传输数据的简单类。因此,它们通常用于在层之间通信,特别是当您有通过消息而不是对象进行通信的SOA架构时。 DTO应该是不可变的,因为它们仅存在于传输信息的目的,而不是改变信息。
您的域对象是一件事情,您的DTO是另一件事情,您需要在演示层中使用的对象是另一件事情。然而,在小型项目中,实施所有这些不同集合并在它们之间进行转换可能不值得那份努力。这取决于您的要求。
您正在设计Web应用程序,但询问自己“我是否可以通过桌面应用程序切换我的Web应用程序?我的服务层是否真正不知道我的演示逻辑?”可能有益于您的设计。以这种方式思考将指导您走向更好的架构。
回答您的问题
- 假设持久化层使用类 myproject.persistence.domain.UserEntity (基于JPA实体)来存储和加载数据到/从数据库。要在视图中显示数据,我会提供另一个类myproject.service.domain.User。我应该在哪里进行转换?用户服务负责在两个类之间转换吗?这真的有助于改善耦合吗?
服务层知道其类(DTO)和其下面的层(假设是持久层)。因此,是的,该服务负责在持久性和自身之间进行翻译。
- User类应该长什么样?它只应包含getter以保持不可变性吗?对于查看编辑现有用户(创建新用户,使用现有用户对象的getter等),这是否会很麻烦?
DTO的理念是仅在传输时使用它们,因此不需要创建新用户等操作。对此,您需要不同的对象。
应该使用相同的DTO类(User)发送请求到服务来修改现有用户/创建新用户还是应该实现其他类?
服务方法可能表达操作,DTO只包含数据作为其参数。另一种选择是使用命令来表示操作,并且还包含DTO。这在SOA架构中很受欢迎,其中您的服务可能仅是一个命令处理器,例如具有一个单独的Execute操作,以ICommand接口作为参数(与每个命令都有一个操作相反)。
如果在myproject.service.domain中使用所有DTO,那么展示层是否会非常依赖于服务层?
是的,覆盖服务层的层将依赖于它。这就是想法。好处是只有该层依赖于它,上下层都不会受影响(与从每个层使用域类发生的情况不同)。
如何处理我的自定义异常?我的当前方法重新抛出大多数“严重”的异常,直到它们由演示层处理(通常记录并告知用户出现了问题)。一方面,我有一个共享的包的问题。另一方面,我仍然不确定这是否可以被认为是“最佳实践”。有什么想法?每个层级都可以有自己的异常。它们从一个层级流动到另一个层级中,封装在下一种异常中。有时,它们将由一个层级处理,该层级将执行某些操作(例如日志记录),然后可能抛出一个不同的异常,上一层必须处理。其他时候,它们可能会被处理并解决问题。例如,考虑连接到数据库时出现的问题。它将引发异常。您可以处理它,并决定在1秒钟后重试,然后可能成功,因此异常不会向上流动。如果重试也失败,异常将被重新抛出,可能一直流到呈现层,您可以优雅地通知用户并要求他重试该层。