【问题标题】:Domain object construction responsibility: Should I use domain objects in service layer interface?域对象构建责任:我应该在服务层接口中使用域对象吗?
【发布时间】:2012-05-27 13:11:39
【问题描述】:
在设计服务层时,我应该在接口契约中使用我的领域对象吗?例如:
public void registerUser(String username, String realName)
VS
public void registerUser(用户用户)
域对象应该在客户端代码中还是在服务外观之后构建?
我正在使用 EJB,我的客户端可能是本地部署的 Web 应用程序、RMI 客户端,也可能是 Web 服务客户端。
【问题讨论】:
标签:
web-services
jakarta-ee
architecture
ejb
【解决方案1】:
域对象应该在客户端代码中还是在后面构建
服务门面?
域对象应在服务端创建。客户端只需传递创建对象所需的所有参数。
对象构造的一般算法是:
- 获取参数
- 验证参数
- 将有效参数传递给对象构造函数以获取有效对象
- 在存储库中注册一个有效的构造对象以进行持久性(如有必要)
如果在客户端创建对象并将其传递给服务,则会出现几个问题:
- 来自客户端的对象可能处于不正确的状态(客户端数据不可信)
- 必须验证对象状态
- 现有对象的验证意味着验证逻辑可能重复,私有字段值验证时可能丢失封装,或者有时违反单一责任原则
- 有时服务必须使用传递的对象属性值作为参数来重构对象
【解决方案2】:
在我看来,从门面层暴露领域对象是不合适的。使用该服务的客户端不应依赖于域对象。最好设计一个数据合约并将它们从外观层公开。它们将仅用于传输数据,即 DTO。
考虑到域对象不仅仅是 DTO,它们可以提供一些无功能的东西。
此外,外观层中的服务暴露了系统的使用,它可能包括多个领域对象的某些部分的协作,有时可能在外观层中的方法的需求和可用的领域对象之间没有合适的映射。
此外,出于网络性能的原因,DTO 及其结构可能需要考虑一些因素。而这些考虑在设计领域对象时通常没有意义。
【解决方案4】:
将域对象作为服务层的一部分传递是一种更简单且更面向对象的方式。
处理用户输入值并构造相关的域对象 - 是 Controller 层的职责
此外,服务层描述了最终客户端使用的API。如果服务层开始使用泛型类型(如字符串、地图等)公开 API,那么麻烦就开始了。
与 DTO 相比,anti-pattern 域对象更适合从控制器层传递到服务层。
【解决方案5】:
从技术上讲,使用一个或另一个没有问题:通过 XSD 的 Web 服务能够支持像字符串这样的原始类型和像用户类这样的复杂对象。
现在,如果您的User 类有20 个属性,而您只需要username 和realName 来注册用户怎么办?在这种特殊情况下,最好使用您的第一种方法,因为如果您不强迫您的客户构建不需要的大型 XML 文档,则需要的带宽更少。
其他情况是您的 User 类根据 JAXB 规则生成复杂且高度嵌套的 XML 文档。这可以为您的客户和复杂的客户实现产生复杂的消息。如果是这种情况,您可以使用更简单的域类版本(可能具有一到两个嵌套级别)作为 DTO 来简化消息交换。
【解决方案6】:
在我看来,服务层一般不应该使用域对象。域是处理业务逻辑、规则和工作流的东西,而服务提供了一个接口。
设计服务层时最简单的原则是“服务方法实现单个用例”。 (用例是一个输入和输出都定义得很好的场景)。
在您的示例中 - registerUser(String username, String realName) - 看起来完全正常。服务将实例化所有需要的领域对象并启动业务操作 - 同时,服务客户端不知道业务逻辑内部(例如,可能有一些特定的用户对象构造等)