Domain driven design 在聚合内部使用存储库与在域服务内部使用存储库的结果是什么

Domain driven design 在聚合内部使用存储库与在域服务内部使用存储库的结果是什么,domain-driven-design,aggregate,repository-pattern,single-responsibility-principle,Domain Driven Design,Aggregate,Repository Pattern,Single Responsibility Principle,我们都听说将存储库注入聚合是一个坏主意,但几乎没有人告诉我们为什么 我将试着在这里写下这样做的所有缺点,这样我们就可以衡量这句话的正确性 我首先想到的是单一责任原则。 的确,通过将存储库注入AR,我们违反了SRP,因为检索和持久化聚合并不是聚合本身的责任。但它只提到了“聚合本身”,而没有提到其他聚合。那么它是否适用于从id引用的存储库聚合中检索?那么如何储存它们呢 我曾经认为聚合甚至不应该知道系统中存在某种持久性,因为它不必存在。可以只为一个过程调用创建聚合,然后将其删除 现在当我想到它时,它是

我们都听说将存储库注入聚合是一个坏主意,但几乎没有人告诉我们为什么

我将试着在这里写下这样做的所有缺点,这样我们就可以衡量这句话的正确性

我首先想到的是单一责任原则。

的确,通过将存储库注入AR,我们违反了SRP,因为检索和持久化聚合并不是聚合本身的责任。但它只提到了“聚合本身”,而没有提到其他聚合。那么它是否适用于从id引用的存储库聚合中检索?那么如何储存它们呢

我曾经认为聚合甚至不应该知道系统中存在某种持久性,因为它不必存在。可以只为一个过程调用创建聚合,然后将其删除

现在当我想到它时,它是不对的,因为聚合根是一个实体,实体只有当它有一些唯一的标识时才有意义。那么,如果不是为了坚持,我们为什么需要独特的身份呢?即使只是记忆中的一个持久性。也许是为了比较,但在我看来,这不是身份背后的主要原因

好的,假设我们使用注入的存储库从聚合内部检索和存储其他聚合。除了违反SRP之外,还有什么其他后果?

当然,对聚合的持久化没有控制是有问题的,而检索是某种延迟加载,出于同样的原因(没有控制),这是不好的

由于没有控制,当我们将同一个聚合持久化几次(只能持久化一次)或将同一个聚合加载一百次(可以加载一次)时,就会出现这种情况,因此性能更差。此外,过时数据也可能存在问题

这些原因实际上取消了将存储库注入聚合的能力

我的主要问题来了——为什么我们可以将存储库注入域服务?

这里的理由不一样吗?这就像将逻辑从聚合中移到单独的函数中,并假装它是不同的东西

老实说,当我开始写这个问题时,我没有很好的答案。但经过几个小时的调查和写作,我终于找到了答案。橡皮鸭调试


不管怎样,我都会为其他有同样问题的人发布这个问题。当然,下面是我的答案。

因此,当我写一个问题时,我发现我的答案已经在写这个问题的过程中了

最好的方法是举例说明:

当我们有一个简单的(表面上)行为,比如单位攻击其他单位,我们可以写这样的东西

unit.attack_unit(other_unit)
问题是,要攻击一个单位,我们必须计算伤害,要做到这一点,我们需要另一个集合,比如武器和盔甲,它们由单位内部的id引用。因为我们不能在聚合中注入存储库,所以我们必须将攻击单元逻辑移动到域服务中,因为我们可以在那个里注入存储库。现在,将它注入域服务和不注入单元聚合之间的区别在哪里

答案是-没有区别。我在问题中描述的所有后果都不会伤害我们。在这两种情况下,我们将加载两个单位一次,攻击单位武器一次,被攻击单位装甲一次。而且不会有过时的数据,即使我们在这个过程中改变了武器对象并将其存储,因为那个武器被检索并存储在一个地方

问题出现在不同的示例中。

让我们创建一个用例,在这个用例中,单位可以在一个过程中攻击游戏中的所有其他单位

问题在于我们如何实施它。如果我们将使用已经定义的单位。攻击单位,我们将在游戏中的所有单位上调用它(对它们进行迭代),那么用于计算伤害的武器将从单位集合中检索,次数等于游戏中的单位数!但它只能被找回一次

不管unit.attack\u unit是单元聚合的方法,还是域服务单元\u attack\u unit。它仍然会是一样的,武器会被加载太多次。要解决这个问题,我们只需更改实现,也可能需要更改接口

现在,至少我们对“将逻辑从聚合方法移动到域服务(因为我们想访问存储库)是否解决了这个问题”有了答案。不,它不会改变任何事情。 如果使用错误,将存储库注入域服务可能和将其注入聚合一样危险

这回答了我的问题,但我们仍然没有解决真正问题的办法

如果我们有两个用例,我们能做什么:一个是单元攻击另一个单元,另一个是单元攻击所有其他单元,而不复制域逻辑

一种方法是将所有需要的聚合作为聚合方法的参数

unit.attack_unit(unit, weapon, armor)
但如果我们需要五个或更多的骨料呢?这不是一个好办法。应用程序逻辑还必须知道,攻击需要所有这些聚合,这就是知识泄漏。当攻击单元实现发生变化时,我们可能还需要更新该方法的接口。那么封装的目的是什么呢

所以,若我们无法访问存储库以获取所需的聚合,那个么我们如何才能将其走私

我们可以通过ID引用聚合来摆脱idea,或者从应用层传递所有需要的聚合(这意味着知识泄漏)

或者这些问题的原因可能是糟糕的建模

攻击其他单位确实是单位的责任,但损失计算是其责任吗?当然不是

也许我们需要另一个物体,比如价值物体近战攻击(武器,盔甲),耶