在领域驱动设计中,似乎有很多一致认为实体不应该直接访问存储库。

这是出自Eric Evans的《领域驱动设计》一书,还是来自其他地方?

对其背后的原因有什么好的解释吗?

编辑:澄清一下:我不是在谈论将数据访问从业务逻辑分离到一个单独层的经典OO实践——我是在谈论DDD中的具体安排,即实体根本不应该与数据访问层对话(即它们不应该持有对Repository对象的引用)。

更新:我把奖金给了BacceSR,因为他的答案似乎最接近,但我仍然对此一无所知。如果这是一个如此重要的原则,网上肯定会有一些关于它的好文章,不是吗?

更新:2013年3月,对这个问题的点赞表明人们对此很感兴趣,即使有很多答案,我仍然认为如果人们对此有想法,还有更多的空间。


当前回答

在理想的情况下,DDD建议实体不应该引用数据层。但是我们并不是生活在理想的世界里。域可能需要引用与它们没有依赖关系的其他业务逻辑域对象。实体以只读的目的引用存储库层来获取值是合乎逻辑的。

其他回答

虽然来晚了,但我还是会说点我的意见。

从性能角度来看,在域模型中抽象REST API操作的存储库和域服务可能是一个重大灾难。我认为无论是域服务(尽管在红皮书中有其他说法!),还是聚合都不应该尝试与它们合作,这两个概念应该只留在应用服务领域,无论你使用Layers还是Hexagon(端口和适配器),它都有与外部世界通信的唯一责任。

通过这种方式,所有昂贵的I/O通信都由一个应用程序服务分配并完全控制。它将:

防止任何类型的性能瓶颈。 在域模型中防止任何类型的全局访问(存储库是全局的)。

建立正确的对象图,在应用服务中使用正确的抓取策略,将纯内存中的对象传递给富域模型。惰性加载会潜入你的代码,并在最痛的地方打击你。

To cite Carolina Lilientahl, "Patterns should prevent cycles" https://www.youtube.com/watch?v=eJjadzMRQAk, where she refers to cyclic dependencies between classes. In case of repositories inside aggregates, there is a temptation to create cyclic dependencies out of conveniance of object navigation as the only reason. The pattern mentioned above by prograhammer, that was recommended by Vernon Vaughn, where other aggregates are referenced by ids instead of root instances, (is there a name for this pattern?) suggests an alternative that might guide into other solutions.

类之间循环依赖的例子(忏悔):

(Time0): Sample和Well这两个类彼此引用(循环依赖)。Well指的是Sample,而Sample指的是Well,这是为了方便(有时是对样品进行循环,有时是对一个板中的所有孔进行循环)。我无法想象样本不会指向它所在的井。

(Time1):一年之后,许多用例实现了....现在有一些情况下,样本不应该指向它所在的井。在一个工作步骤内有临时板。这里的孔指的是样品,而样品又指的是另一个盘子上的孔。正因为如此,当有人试图实现新功能时,有时会出现奇怪的行为。渗透需要时间。

我也从上面提到的这篇关于惰性加载的负面方面的文章中得到了帮助。

对我来说,这似乎是与OOD相关的一般良好实践,而不是DDD特有的。

我能想到的原因是:

关注点分离(实体应该与它们的持久化方式分离。因为根据使用场景的不同,可能存在多种策略,其中相同的实体将被持久化) 从逻辑上讲,实体可以在存储库操作的级别之下的级别中看到。较低级别的组件不应该具有较高级别组件的知识。因此,条目不应该有关于存储库的知识。

这是出自Eric Evans的《领域驱动设计》一书,还是来自其他地方?

都是老东西了。埃里克的书让它更热闹了。

对其背后的原因有什么好的解释吗?

原因很简单——当人类面对模糊相关的多种情境时,大脑会变得脆弱。它们导致了歧义(美国在南/北美意味着南/北美),歧义导致了每当大脑“接触到”信息时,信息的不断映射,这总结起来就是糟糕的生产力和错误。

业务逻辑应该尽可能清晰地反映出来。外键、归一化、对象关系映射是完全不同的领域——那些东西是技术上的,与计算机有关。

打个比方:如果你正在学习如何写字,你就不应该被笔是在哪里制造的,为什么墨水能在纸上保持,纸是什么时候发明的,以及中国还有哪些其他著名的发明。

编辑:澄清一下:我不是在谈论将数据访问从业务逻辑分离到一个单独层的经典OO实践——我是在谈论DDD中的具体安排,即实体根本不应该与数据访问层对话(即它们不应该持有对Repository对象的引用)。

原因和我上面提到的一样。这里只是更进一步。如果实体可以(至少接近)完全忽略持久性,为什么它们应该部分忽略持久性?我们的模型所包含的与领域无关的问题更少——当我们不得不重新解释它时,我们的大脑获得了更多的喘息空间。

在所有这些单独的层出现之前,我学会了编写面向对象的编程,我的第一个对象/类确实直接映射到数据库。

最后,我添加了一个中间层,因为我必须迁移到另一个数据库服务器。同样的场景我已经看过/听说过好几次了。

我认为分离数据访问(又名。“存储库”)来自您的业务逻辑,是那些已经被重新发明了几次的东西之一,尽管领域驱动设计书,使它有很多“噪音”。

我目前使用3层(GUI,逻辑,数据访问),像许多开发人员一样,因为这是一个很好的技术。

将数据分离到存储库层(又名数据访问层),可以看作是一种良好的编程技术,而不仅仅是一种规则。

像许多方法一样,一旦您理解了它们,您可能希望从NOT implemented开始,并最终更新您的程序。

引用: 《伊利亚特》不完全是荷马发明的,《卡米娜·布兰娜》也不完全是卡尔·奥尔夫发明的,在这两种情况下,把别人的工作放在一起的人得到了荣誉;-)