domain-driven-design - DDD 子实体验证

标签 domain-driven-design aggregateroot

哪一层应该负责检查数据库中某个实体的存在? 假设我有一个聚合订单,该订单可以包含多个项目。从逻辑上讲,我只能将现有商品添加到订单中。

在应用服务中是不是应该这样写:

var item = ItemRepository.GetByID(id);

//throws exception if the item is null
order.AddItem(item);

//validate item existence inside aggregate function
order.AddItem(item, IItemRepository repo);

最佳答案

都不是,真的。

实体不跨越聚合边界。实体是聚合的一部分,在这种情况下聚合管理自己的生命周期,或者项目是某个其他聚合的一部分,在这种情况下你不共享实体,你共享一个引用。

order.AddItem(id)

聚合的部分定义是不同聚合的变化可以相互独立发生。换句话说,这个聚合无法知道“现在”那个聚合中发生了什么。

换句话说,您无法确保跨聚合边界的事务一致性。

如果你愿意接受数据竞争,正确的答案是使用领域服务来查询边界外的状态。

interface InventoryService{
    boolean currentlyInStock(Item id);
}

// ...

order.addItem(id, inventoryService);

几点: 使用域服务 而不是传入其他存储库,因为它可以更好地传达正在发生的事情。领域服务作为聚合实际需要的契约的描述。此外,通过拒绝传递存储库,您排除了订单聚合尝试写入项目存储库的任何可能性。

(此域服务的简单实现是将调用转发到存储库,但订单聚合不需要知道这一点)。

在这种情况下,域服务应该根据库存的可用性选择一个 Action 来“提供帮助”——也许聚合应该抛出,也许聚合应该为低抛出批量订单/低优先级购买者,但当订单超过一百万美元时使用不同的规则。弄清楚这一点是订单的工作,领域服务只是提供数据。

鉴于数据竞争,一些误报可能会被漏掉;检测和缓解是个好主意。

如果您不愿意接受数据竞争(您确定吗?亚马逊一直接受缺货商品的订单...),那么您需要重新考虑模型的设计,以及您有哪些设置您的聚合边界。

模型的空设计是将所有业务状态捕获到一个聚合中;它自己的状态在内部是一致的,但它可能与外部状态不一致。当您开始将模型分割成单独的聚合时,您正在做出相同的断言——聚合需要在内部保持一致,但它可能与聚合外部的状态不一致。

如果这 Not Acceptable ,那么您可以将母亲的照片贴到墙上,并直接在记录簿中实现您的业务规则(即,在您的 RDBMS 中进行约束)。

关于domain-driven-design - DDD 子实体验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37911193/

相关文章:

domain-driven-design - 有界上下文、子域和无处不在的语言

domain-driven-design - Enterprise Integration Patterns 书是否补充了 DDD?

domain-driven-design - 领域驱动设计方法重复

c# - 应用层和契约

domain-driven-design - CQRS 中的聚合

dependency-injection - 如何从域对象内分派(dispatch)域事件?

domain-driven-design - 查找值是否应该建模为聚合根?

domain-driven-design - 领域驱动设计

.net - 存储库模式和聚合根模式和 Entity Framework

c# - 组合属性 setter "init and private"(C#9)