DDD:我真的需要加载聚合中的所有对象吗? (表现问题)

时间:2016-06-06 16:31:59

标签: domain-driven-design

在DDD中,存储库加载整个聚合 - 我们要么加载所有聚合,要么加载所有聚合。这也意味着应该避免延迟加载。

我关心的是表现方面。如果这会导致数千个对象加载到内存中会怎样?例如,Customer的汇总返回了一万Orders

在这种情况下,是否意味着我需要重新设计并重新考虑我的聚合? DDD是否就此问题提出建议?

2 个答案:

答案 0 :(得分:25)

看看这个来自弗农的三篇文章的Effective Aggregate Design系列。我发现它们非常有用,可以了解何时以及如何设计较小的聚合而不是大型聚合聚合。

修改

我想举几个例子来改进我以前的答案,随时分享你对它们的看法。

首先,关于聚合的快速定义(取自Scott emlet的模式,领域驱动设计的原理和实践一书)

  

实体和值对象协作以形成满足域模型中不变量的复杂关系。处理大型互连的对象关联时,在对域对象执行操作时,通常很难确保一致性和并发性。 Domain-Driven Design具有Aggregate模式以确保一致性并为对象图定义事务并发边界。大型模型由不变量分割,并分组为实体和值对象的聚合,这些实体和值对象被视为概念整体。

让我们举一个例子,看看实际中的定义。

简单示例

第一个示例显示了在对域对象执行操作时,定义聚合根有助于确保一致性。

鉴于下一个业务规则:

  

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

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

如果我们说Bid也是一个单独的聚合根,那么您将拥有BidsRepository,并且可以轻松完成:

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

您在不传递已定义的业务规则的情况下保存了出价。但是,将Auction作为唯一的Aggregate Root,您正在执行您的设计,因为您需要执行以下操作:

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

因此,您可以在方法placeBid中检查您的不变量,如果他们想要发布新的出价,则任何人都可以跳过它。

很明显,出价状态取决于拍卖的状态。

复杂示例

回到您与客户关联的订单示例,看起来没有不变量使我们定义一个由客户及其所有订单组成的巨大聚合,我们可以通过标识符引用保持两个实体之间的关系。通过这样做,我们避免在获取客户时加载所有订单以及减轻并发问题。

但是,现在业务定义了下一个不变量:

  

我们希望为顾客提供口袋,以便他们可以用钱购买产品。因此,如果客户现在想要购买产品,则需要有足够的资金来购买产品。

这样说,pocket是Customer Aggregate Root中的一个VO。现在看来,有两个独立的聚合根,一个用于客户,另一个用于订单不是最好的满足新的不变量,因为我们可以在不检查规则的情况下保存新订单。看起来我们被迫将客户视为根。这将影响我们的性能,可伸缩性和并发性问题等。

解决方案?最终的一致性。如果我们允许客户购买产品怎么办?也就是说,有一个订单的聚合根,所以我们创建订单并保存它:

var newOrder = new Order(customerId, ...);
orderRepository.save(newOrder);

我们在创建订单时发布事件,然后我们异步检查客户是否有足够的资金:

class OrderWasCreatedListener:
    var customer = customerRepository.findOfId(event.customerId);
    var order = orderRepository.findOfId(event.orderId);
    customer.placeOrder(order); //Check business rules
    customerRepository.save(customer);

如果一切都很好,我们已经满足了我们的不变量,同时保持我们的设计,我们在开始时每个请求只修改一个聚合根。否则,我们会向客户发送一封电子邮件,告诉她资金不足的问题。我们可以通过添加她可以用当前预算购买的电子邮件替代选项以及鼓励她收取口袋来推进它。

考虑到用户界面可以帮助我们避免让客户在没有足够资金的情况下付款,但我们不能盲目信任用户界面。

希望您发现这两个示例都很有用,如果您找到更好的解决方案,请告诉我们: - )

答案 1 :(得分:15)

  

在这种情况下,是否意味着我需要重新设计并重新考虑我的聚合?

几乎可以肯定。

聚合设计的驱动因素不是结构,而是行为。我们并不关心用户有数千个订单"。我们关心的是当您尝试处理更改时需要检查哪些状态 - 您需要加载哪些数据才能知道更改是否有效。

通常情况下,您会意识到更改订单不会(或者不应该)取决于系统中其他订单的状态,这是两个不同订单的良好指示不应该是同一集合的一部分。