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

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

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

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

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

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


当前回答

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

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

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

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

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

其他回答

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

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

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

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

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

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

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

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

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

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

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

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

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

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

我能想到的原因是:

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

这里有点混乱。存储库访问聚合根。聚合根是实体。这样做的原因是关注点的分离和良好的分层。这在小型项目中没有意义,但如果您在一个大型团队中,您会说:“您通过产品存储库访问产品。Product是实体集合的聚合根,包括ProductCatalog对象。如果你想要更新ProductCatalog,你必须通过ProductRepository。”

通过这种方式,您在业务逻辑和内容更新的位置上有非常非常清晰的分离。你不会有一个孩子独自编写了整个程序,对产品目录做了所有这些复杂的事情,当涉及到将其集成到上游项目时,你坐在那里看着它,意识到这一切都必须抛弃。这也意味着当人们加入团队,添加新功能时,他们知道该去哪里以及如何组织程序。

But wait! Repository also refers to the persistence layer, as in the Repository Pattern. In a better world an Eric Evans' Repository and the Repository Pattern would have separate names, because they tend to overlap quite a bit. To get the repository pattern you have contrast with other ways in which data is accessed, with a service bus or an event model system. Usually when you get to this level, the Eric Evans' Repository definition goes by the way side and you start talking about a bounded context. Each bounded context is essentially its own application. You might have a sophisticated approval system for getting things into the product catalog. In your original design the product was the center piece but in this bounded context the product catalog is. You still might access product information and update product via a service bus, but you must realize that a product catalog outside the bounded context might mean something completely different.

Back to your original question. If you're accessing a repository from within an entity it means the entity is really not a business entity but probably something that should exist in a service layer. This is because entities are business object and should concern themselves with being as much like a DSL (domain specific language) as possible. Only have business information in this layer. If you're troubleshooting a performance issue, you'll know to look elsewhere since only business information should be here. If suddenly, you have application issues here, you're making it very hard to extend and maintain an application, which is really the heart of DDD: making maintainable software.

对评论1的回应:好问题。所以并不是所有的验证都发生在领域层。夏普有一个属性“DomainSignature”,它可以做你想要的。它是持久性感知的,但是作为一个属性可以使域层保持干净。它确保在您的示例中,没有名称相同的重复实体。

But let's talk about more complicated validation rules. Let's say you're Amazon.com. Have you ever ordered something with an expired credit card? I have, where I haven't updated the card and bought something. It accepts the order and the UI informs me that everything is peachy. About 15 minutes later, I'll get an e-mail saying there's a problem with my order, my credit card is invalid. What's happening here is that, ideally, there's some regex validation in the domain layer. Is this a correct credit card number? If yes, persist the order. However, there's additional validation at the application tasks layer, where an external service is queried to see if payment can be made on the credit card. If not, don't actually ship anything, suspend the order and wait for the customer. This should all take place in a service layer.

不要害怕在服务层创建可以访问存储库的验证对象。让它远离域层。