在哪里放置业务逻辑?服务(Service)还是数据访问对象(DAO)?

7
给定:
  1. Spring MVC - Hibernate。
  2. Controller -> Service -> DAO
  3. 现在我有一个方法,它从数据库中检索某些内容,并且每次执行此操作时,都必须执行另一个方法,比如“processList”(例如根据一些屏幕参数更改列表中的某些值)。
问题:
  1. 我应该将这个“processList”放在哪一层?(控制器,服务或DAO?为什么)
我现在真的需要一些关于j2ee的澄清,我知道MVC在各种语言中都是相同的,但我只是需要确认一下:)如果我在.NET中做到这一点,我无疑会将其放在服务中。

1
这真的很取决于processList方法从逻辑视图上在做什么。 - Pavel Horal
1
绝对不会放在控制器中。我会把它放在服务中,但你(或Java社区)对服务的理解与我的责任感可能存在巨大差异。 - tereško
我认为这个问题属于“太泛泛”的SO问题类别(即它有可能被关闭)。尽管如此,我还是添加了一个相对详细的描述,说明我在面对“代码应该放在哪里”的决策时遵循的规则。 - Pavel Horal
1个回答

19

这实际上取决于 processList 在做什么。并没有通用的规则,但是有一些我尝试遵循的规则:

  1. 不要在同一层级的主要对象之间进行调用。
    • ManagementServiceImpl 不应该调用 NotificationServiceImpl
  2. 不要在对象之间创建循环依赖关系。
    • 其与上述规则非常类似。
  3. 如果你发现自己在多个主要对象之间重复一些逻辑,请尝试重新组织代码并将其提取到专门的逻辑类中(这也将改善单元测试)。
    • 例如: UserUpdateHandler 或者 NotificationDispatcher (这些仍然归属于服务层 -> 没有其他人被允许调用它们)...
  4. 将代码放在逻辑上正确的位置。
    • 不要因为某个类需要执行某些操作而分心。可能并不是代码的正确位置。
  5. 在需要之前不要编写过度概括的代码。
    • 这称为“过早概括”,这是一种不良做法,类似于“过早优化”。现在保存几行代码可能会导致未来的头发被拔掉。
  6. 始终编写能够概括的代码。
    • 这与上述规则并不矛盾。这表示 - 始终具有概括性的编写,但如果不需要,则不必费心编写。预先考虑,但不一定立即行动。
  7. 将业务逻辑留给服务层、数据持久层留给数据层以及用户交互逻辑留给演示层。
    • 不要尝试在服务层中解析用户输入。这与在电子商务应用程序中计算最终价格不属于演示层非常相似。

processList 的一些示例:

  • 示例 I - 通过Hibernate#initialize获取其他关系
    • 这是服务层和数据访问对象(DAO)之间的交集。在旧项目中,我们有专门的FetchHandler类(由服务层拥有)。在新项目中,我们完全将此留给DAO。
  • 示例 II - 遍历列表并向结果添加业务验证消息
    • 毫无疑问是服务层。
  • 示例 III - 遍历列表并根据验证错误准备UI消息
    • 展示层。

副注:

  • MVC是与三层架构不同的东西。
  • M模型覆盖了所有三个层次。表示层包括V视图和C控制器。

在你的第一个观点中,你是说一个服务永远不应该调用另一个服务吗? - Usman Mutawakil
这是我们在应用程序中所遵循的。如果我们需要重复使用某些逻辑,通常会将其抽象成另一个组件。 - Pavel Horal
我不确定我会使用“从不”这个词。例如,Login/UserVerificationService。在允许任何服务中进行CRUD操作之前,您可能需要调用此服务,同时它具有自己的关联控制器和视图,用于专用登录页面。电子邮件服务也是如此。我认为这些事情超出了辅助或实用程序类的范畴,绝对是其他服务所必需的。 - Usman Mutawakil
“从未”可能有点过于绝对。但我们还没有违反这个规则。您使用了两个我会以不同方式实现的例子。安全是由AOP或安全组件处理的横切关注点(即使登录本身可能由业务服务处理)。 - Pavel Horal
对于电子邮件,我们使用 NotificationDispatcher 组件。你可能会说我们在“欺骗规则”,因为我们只是将 EmailService 重命名。而且你并没有离真相太远。但我们只是遵守我们的规则,这个组件只从 *Service 类中调用。所以更高层次的组件需要通过业务服务进行访问。 - Pavel Horal
这个规则背后的主要原因是我们决定不想有循环依赖。而这个规则使得它更容易实现。如果没有这样的规则,你会被诱惑在每一步之间调用服务。不仅如此 - 我们在每一层都使用这个规则(禁止从dao调用dao等)。同时,当我们开始遵循这个规则时,我们不知怎么地被迫制作更好的封装组件... 这让你对设计思考两次,这是好的 :)。 - Pavel Horal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接