域对象中的存储库

时间:2016-06-03 13:07:27

标签: domain-driven-design microservices

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

2 个答案:

答案 0 :(得分:5)

是的,我认为这是错误的。

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

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

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

答案 1 :(得分:5)

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

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

  

必须始终在拍卖结束前设置获胜的竞价。如果在拍卖结束后放置中标,则域处于无效状态,因为已经破坏了不变量并且模型未能正确应用域规则。

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

如果您有BidsRepository,则可以轻松完成:

var newBid = new Bid(money);
BidsRepository->save(newBid);

您在不传递已定义的业务规则的情况下保存了出价。但是,将存储库仅用于聚合根,您将强制执行您的设计,因为您需要执行以下操作:

var newBid = new Bid(money);
auction.placeBid(newBid);
auctionRepository.save(auction);

因此,您可以在方法placeBid中检查您的不变量,如果他们想要设置新的出价,则无人可以跳过它。之后,您可以将信息保存到任意数量的表中,这是一个实现细节。

其次,你说如果将存储库注入Domain类是错误的。这里有一个简单的解释:

  

存储库应该依赖于它返回的对象,而不是相反。这样做的原因是你的"域对象" (稍后会详细介绍)可以存在(并且应该是可测试的),而不会被加载或保存(即,依赖于存储库)。

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

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