DDD 子实体验证
DDD child entity validation
哪一层应该负责检查数据库中某个实体的存在?
假设我有一个聚合订单,该订单可以包含多个项目。逻辑意味着我只能将现有项目添加到订单中。
在应用服务中是不是应该这样写:
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)
聚合的部分定义是不同聚合的变化可以相互独立发生。换句话说,this 聚合不可能知道 that 聚合 "now".
中发生了什么
换句话说,您无法确保跨聚合边界的事务一致性。
如果你愿意接受数据竞争,正确的答案是使用域服务来查询边界外的状态。
interface InventoryService{
boolean currentlyInStock(Item id);
}
// ...
order.addItem(id, inventoryService);
几点:
使用域服务 而不是传入其他存储库,因为它可以更好地传达正在发生的事情。领域服务作为聚合实际需要的契约的描述。此外,通过拒绝传递存储库,您排除了订单聚合尝试写入项目存储库的任何可能性。
(这个域服务的简单实现只是将调用转发到存储库,但订单聚合不需要知道)。
域服务,在这种情况下,应该 而不是 "helping" 根据库存的可用性选择一个动作——也许聚合应该抛出,也许合计应该针对小批量 orders/low 优先购买者,但当订单超过 100 万美元时使用不同的规则。弄清楚这一点是订单的工作,域服务只是提供数据。
考虑到数据竞争,一些误报可能会被漏掉;检测和缓解是个好主意。
如果您不愿意接受数据竞争(您确定吗?亚马逊一直接受缺货商品的订单...),那么您需要重新考虑模型的设计,以及在哪里您已设置聚合边界。
模型的空设计是将所有业务状态捕获到一个聚合中;它自己的状态在内部是一致的,但它可能与外部状态不一致。当您开始将模型分割成单独的聚合时,您正在做出相同的断言——聚合需要在内部保持一致,但它可能与聚合外部的状态不一致。
如果那不可接受,那么您可以将母亲的照片挂在墙上,并直接在记录簿中实施您的业务规则(即,在您的 RDBMS 中进行约束)。
哪一层应该负责检查数据库中某个实体的存在? 假设我有一个聚合订单,该订单可以包含多个项目。逻辑意味着我只能将现有项目添加到订单中。
在应用服务中是不是应该这样写:
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)
聚合的部分定义是不同聚合的变化可以相互独立发生。换句话说,this 聚合不可能知道 that 聚合 "now".
中发生了什么换句话说,您无法确保跨聚合边界的事务一致性。
如果你愿意接受数据竞争,正确的答案是使用域服务来查询边界外的状态。
interface InventoryService{
boolean currentlyInStock(Item id);
}
// ...
order.addItem(id, inventoryService);
几点: 使用域服务 而不是传入其他存储库,因为它可以更好地传达正在发生的事情。领域服务作为聚合实际需要的契约的描述。此外,通过拒绝传递存储库,您排除了订单聚合尝试写入项目存储库的任何可能性。
(这个域服务的简单实现只是将调用转发到存储库,但订单聚合不需要知道)。
域服务,在这种情况下,应该 而不是 "helping" 根据库存的可用性选择一个动作——也许聚合应该抛出,也许合计应该针对小批量 orders/low 优先购买者,但当订单超过 100 万美元时使用不同的规则。弄清楚这一点是订单的工作,域服务只是提供数据。
考虑到数据竞争,一些误报可能会被漏掉;检测和缓解是个好主意。
如果您不愿意接受数据竞争(您确定吗?亚马逊一直接受缺货商品的订单...),那么您需要重新考虑模型的设计,以及在哪里您已设置聚合边界。
模型的空设计是将所有业务状态捕获到一个聚合中;它自己的状态在内部是一致的,但它可能与外部状态不一致。当您开始将模型分割成单独的聚合时,您正在做出相同的断言——聚合需要在内部保持一致,但它可能与聚合外部的状态不一致。
如果那不可接受,那么您可以将母亲的照片挂在墙上,并直接在记录簿中实施您的业务规则(即,在您的 RDBMS 中进行约束)。