Domain driven design 域对象中的存储库

Domain driven design 域对象中的存储库,domain-driven-design,microservices,Domain Driven Design,Microservices,我看过很多关于这个话题的讨论,但是我没有得到一个令人信服的答案。一般的建议是不要在域对象中包含存储库。聚合根呢?赋予根操作组合对象的责任难道不是正确的吗? 例如,我有一个处理发票的微服务。发票是具有不同产品的聚合根。此服务不要求提供有关单个产品的详细信息。我有两个表,一个用于存储发票详细信息,另一个用于存储这些发票的产品。我有两个对应于表的存储库。我已经在invoice域对象中注入了产品存储库。这样做不对吗 是的,我认为这是错误的 域应该匹配真实的业务模型,而不应该关心数据是如何持久化的。即使内

我看过很多关于这个话题的讨论,但是我没有得到一个令人信服的答案。一般的建议是不要在域对象中包含存储库。聚合根呢?赋予根操作组合对象的责任难道不是正确的吗?
例如,我有一个处理发票的微服务。发票是具有不同产品的聚合根。此服务不要求提供有关单个产品的详细信息。我有两个表,一个用于存储发票详细信息,另一个用于存储这些发票的产品。我有两个对应于表的存储库。我已经在invoice域对象中注入了产品存储库。这样做不对吗

是的,我认为这是错误的

域应该匹配真实的业务模型,而不应该关心数据是如何持久化的。即使内部数据存储在多个表中,这也不会以任何方式影响域对象

加载聚合根目录时,还应一次性加载相关实体。例如,如果您在.NET上,则可以在实体框架中使用
Include
关键字轻松实现这一点。通过加载所有数据,您可以确保在任何给定时间都有业务实体的完整表示,并且不再需要查询数据库


相关实体中的任何更改都应与聚合根一起保留在一个原子操作中(通常使用事务)。

根据您的问题中的DDD原则,我发现了一些错误。让我试着澄清一些概念,以帮助你们

首先,您提到您有一个聚合根,即Invoice,然后是两个不同的存储库。拥有聚合根意味着对聚合所包含的实体的任何更改都应该通过聚合根执行。为什么?这是因为您需要满足一些应用于这些实体之间关系的业务规则(不变量)。例如,给定下一个业务规则:

中标的拍卖出价必须始终在拍卖结束前进行。如果在拍卖结束后进行中标,则域处于无效状态,因为不变量已被破坏,并且模型未能正确应用域规则

这里有一个由拍卖和出价组成的聚合,其中拍卖是聚合根

如果您有一个
bidsresposition
,您可以轻松地执行以下操作:

var newBid = new Bid(money);
BidsRepository->save(newBid);
您在保存出价时未通过定义的业务规则。但是,将存储库仅用于聚合根目录时,您将强制执行您的设计,因为您需要执行以下操作:

var newBid = new Bid(money);
auction.placeBid(newBid);
auctionRepository.save(auction);
因此,您可以在方法
placeBid
中检查您的不变量,如果想要进行新的出价,任何人都不能跳过它。之后,您可以将信息保存到任意多个表中,这是一个实现细节

第二,您说将存储库注入域类是错误的。下面是一个简短的解释:

存储库应该取决于它返回的对象,而不是相反。原因是您的“域对象”(稍后将详细介绍)可以存在(并且应该是可测试的),而无需加载或保存(即,对存储库具有依赖性)

基本上,您的设计表明,为了获得发票,您需要提供一个MySQL/Mongo/XXX实例连接,这是一个基础架构细节。您的域不应该知道它是如何持久化的。您的域知道类似于拍卖和出价场景中的行为


这些概念只是帮助您创建易于维护的代码,并帮助您应用最佳实践,如SRP(单一责任原则)。

为什么否决?可能重复@ArulKumaran我不确定为什么有人否决,但我相信这是因为您在寻找“令人信服的答案”当你似乎不了解DDD的基本知识时,阻止你在战术中使用反模式。我想如果你打开“红皮书”,你会在那里找到所有答案,这里没有什么新鲜事。不要认为这是一种冒犯。