【问题标题】:Using multiple services or multiple repositories within service?在服务中使用多个服务或多个存储库?
【发布时间】:2020-02-08 22:42:33
【问题描述】:
假设我们有两个实体,EntityA 和 EntityB。两个实体都有一个用于查询数据库的存储库EntityARepository 和EntityBRepository。他们也有服务,EntityAService 和EntityBService。
现在EntityBService中有一个方法,同样需要使用EntityA。这样做的正确方法是什么?
-
EntityBService 应该直接使用EntityARepository 吗?
-
EntityBService 是否应该使用EntityAService?
我可以看到直接使用存储库可能非常方便,但是当不仅有两个实体时,它似乎有点混乱。
是否有围绕这个主题的共同设计模式或建议?
【问题讨论】:
标签:
spring
spring-boot
spring-data
domain-driven-design
【解决方案1】:
TLDR;视情况而定!
如果您尝试遵循领域驱动设计,我认为区分service 和repository 是个好主意。有不同的定义可能在细节上有所不同,但我会坚持 Martin Fowler 的 Repository:
(...) 存储库在域和数据映射层之间进行调解,就像内存中的域对象集合一样。 (...)
对于service:
服务层从连接客户端层的角度定义了应用程序的边界 [Cockburn PloP] 及其可用操作集。 它封装了应用程序的业务逻辑,控制事务并在其操作的实现中协调响应。
值得指出的是,service 不仅仅是repository + 业务逻辑。对于大多数简单的场景repository 只是结束了访问单个数据库,但是对于高级场景创建单个实体可能需要访问多个数据库,所以repository 的角色是从service 层中删除这个woffle。
以下是你能想到的:
- 如果您想要做的只是获取
EntityA 而没有与EntityA 相关的任何业务逻辑,则直接在EntityBService 中使用EntityARepository。这是一个简单的例子:
EntityBService 对EntityB 执行操作,但它完全取决于EntityA 状态。
- 如果请求
EntityA涉及到一些与EntityA相关的业务逻辑,则在EntityBService中使用EntityAService。这是一个简单的例子:
EntityBService 对 EntityB 和 EntityA 执行操作,如果不存在则需要稍后创建 - 创建涉及业务逻辑,例如检查是否允许(例如基于用户角色)。
【解决方案2】:
是否有围绕这个主题的共同设计模式或建议?
一种常见的做法是避免更改存储在同一事务中不同位置的实体。确保您永远不会这样做的一种方法是在任何事务中只让一个实体处于活动状态。
所以我们可能合理地拥有实体 A,以及我们需要从 B 获得的信息的副本。或者我们可能拥有实体 B,以及我们需要从 A 获得的信息的副本。
对于我们不更改任何一个实体的用例,我们可以只使用数据的副本。
在我们使用 entityA 和 recent-copy-of-B 的情况下,我们知道 entityA 来自存储库。 recent-copy-of-B 来自哪里?嗯,它来自一个看起来像存储库的抽象,除了 (a) 它获取值,而不是实体,并且 (b) 它是只读的。
recent-copy-of-B 看起来像一个 DTO。虽然您可能在任何地方都使用相同的版本,但为不同的用例创建专门的版本实际上是安全的(因为值是不可变的)。