给定Order聚合,它由Order和OrderItems组成。订单是聚合根。下订单是唯一需要事务以实现数据一致性的方案。或者至少有很多场景涉及按顺序更改数据和不需要事务的orderItems。
在DDD之前,事务是在创建方法期间做出的决定。您通常会考虑此方法或行为是否需要在事务中以保持一致性。
DDD建议为聚合根公开的每个可能行为进行交易。这实际上可能导致更多的死锁。
我认为我不必在聚合根中实现每个数据更改行为都在事务中。
例如,如果您只更改运送详细信息,为什么还需要交易。如果您只删除订单商品,为什么还需要交易。因此对于方法Order.UpdateOrder和Order.DeleteOrderItem,我们不应该考虑事务。
我错过了什么吗?
答案 0 :(得分:0)
是的,我想你错过了什么。如果你正确地设计你的聚合,特别是如果你把它们做得足够小,那么你就不会有无意义的交易,至少不会有很多交易。您的聚合应具有高度内聚性,因此同时修改的属性/状态应保持在同一聚合中;这是聚合点,用于保护对状态的并发访问。
如果您的聚合比它们应该更大,那么聚合force-field
太宽而且没有内聚的命令会被无序序列化。
关于orders
,不会有很多失败的交易,因为添加order item
并同时更改order shipping details
的可能性非常低。
无论如何,由于任何情况下的交易都不会发生死锁,但可能会发生交易失败。
答案 1 :(得分:0)
在最纯粹的形式中,您的存储库只有很少的方法:
public interface IAggregateRepository
{
AggregateRoot Get(Guid id);
void Save(AggregateRoot instance);
}
此方案中的用例/命令之间没有区别。您不一定知道是否需要交易。
另一个考虑因素是应该在集成/应用程序层中处理事务。这使得事务处理进一步远离域交互。
您可以让应用程序了解特定用例,并决定放弃某些命令的事务。但是,这可能是值得的更多努力。
最后一点是,交易并不总是只关于你正在做什么,而是关于其他正在做什么。
我会谨慎行事,而是使用交易。如果有一个特定的理由不使用交易,那么这当然是一种选择,但这似乎更适合读取。
答案 2 :(得分:0)
如果您需要在系统中的某个位置仅更新运输详细信息,那么您可能会使用不同的聚合来使用不同的有界上下文,让我们将其称为ShippingRequest
,只有两个字段OrderId
和ShippingDetails
,最终可能会或可能不会映射到同一个基础表。然后,这个聚合由事务隐式覆盖,因为它只映射到一个表。
或者你可能不想关心所有这些有限的上下文,无处不在的语言和聚合,因为DDD不值得实现,而且一些传统的CRUD /事务脚本方法就足够了。
问题还在于,DDD经常被解释为像这样的错误例子。 Aggregate定义了一些一致性边界,一些代表业务规则的不变量。因此,在订单作为汇总的情况下,让我们说订单被分配了最大允许总数,不得超过该总数。比这变成了自然聚合,因为无论何时想要添加或删除项目,都必须检索整个聚合并将其更新为单个原子单元。