仓储模式下如何让领域层管控实体删除规则——以订单无订单项方可删除为例
让领域层强制执行订单删除规则的可行方案
这个问题问得非常到位——核心就是要把业务规则的控制权完全收回到领域层,彻底避免调用方(比如ASP.NET控制器)绕过规则直接执行删除操作。结合你目前的仓储+工作单元模式,有几个非常贴合的实现方式:
方案1:将删除规则封装到Order实体中(最推荐)
和你创建/更新订单的思路保持一致:把业务行为(包括删除的规则校验)完全封装在实体内部,外部只能调用实体暴露的合法方法,无法直接操作删除动作。
首先修改Order实体,添加删除相关的方法:
public class Order { public int Id { get; private set; } // 用只读集合防止外部直接修改订单项 public IReadOnlyCollection<OrderItem> Items { get; private set; } = new List<OrderItem>(); // 如果是软删除场景,可以加这个属性 // public bool IsDeleted { get; private set; } // 其他业务方法(比如创建订单的构造函数、更新订单的方法)... /// <summary> /// 执行订单删除操作,内部校验规则 /// </summary> /// <exception cref="InvalidOperationException">当订单包含订单项时抛出</exception> public void Delete() { if (Items.Any()) { throw new InvalidOperationException("无法删除包含订单项的订单,请先移除所有订单项"); } // 如果是软删除,在这里标记状态: // IsDeleted = true; } }
然后修改仓储的Remove方法,让它先调用实体的Delete方法(触发规则校验),再执行持久化操作:
public class OrderRepository : IOrderRepository { private readonly YourDbContext _dbContext; public OrderRepository(YourDbContext dbContext) { _dbContext = dbContext; } public Order GetById(int orderId) { // 必须Include订单项,否则校验会出错 return _dbContext.Orders .Include(o => o.Items) .FirstOrDefault(o => o.Id == orderId); } public void Remove(Order order) { // 先触发实体的删除规则校验 order.Delete(); // 如果是硬删除: _dbContext.Orders.Remove(order); // 如果是软删除,不需要Remove,只需要SaveChanges即可: // _dbContext.Entry(order).State = EntityState.Modified; } }
这样调用方的代码完全不需要修改,还是原来的流程,但规则已经被领域层强制执行了:
var order = unitOfWork.Orders.GetById(1); unitOfWork.Orders.Remove(order); unitOfWork.Complete();
如果订单不符合删除条件,会直接抛出异常,调用方只需要捕获异常并处理(比如返回给前端错误提示)即可,完全不用关心校验逻辑。
方案2:在仓储层直接调用实体的校验方法
如果你不想修改实体的结构,也可以在实体中添加一个规则校验方法,然后让仓储在执行删除前调用这个方法:
public class Order { // ...其他属性 /// <summary> /// 校验订单是否可以被删除 /// </summary> /// <returns>符合条件返回true,否则false</returns> public bool CanBeDeleted() { return !Items.Any(); } }
然后仓储的Remove方法做如下修改:
public void Remove(Order order) { if (!order.CanBeDeleted()) { throw new InvalidOperationException("订单包含订单项,无法删除"); } _dbContext.Orders.Remove(order); }
这个方案的效果和方案1一致,只是把校验逻辑和删除动作分开了,适合不需要封装删除行为的场景。
额外注意点
- 一定要确保查询订单时Include关联的OrderItems,否则实体的Items集合是空的,校验会失效。
- 如果是分布式场景,建议在数据库层面也添加一个约束(比如删除订单时检查是否存在关联的OrderItem),作为最后一道防线,避免并发场景下的数据不一致。
- 不要让调用方直接操作DbContext,必须通过仓储和工作单元,这样才能保证规则被统一执行。
内容的提问来源于stack exchange,提问作者Álvaro García
相关产品推荐
相关产品推荐

