Clean Architecture中涉及外部状态的领域规则验证方案咨询
解决Clean Architecture中领域规则依赖外部状态的冲突方案
针对仓库安全规则验证的冲突问题,有几种成熟的Clean Architecture兼容方案,既保证领域层封装核心规则,又避免依赖基础设施层:
1. 领域服务+依赖倒置原则
在领域层定义抽象的安全检查服务接口,将外部状态查询逻辑抽象出来,实体类依赖该抽象而非具体实现:
- 领域层示例代码:
// 领域层抽象接口 public interface IWarehouseSafetyChecker { bool IsPositionSafeForProduct(Position targetPos, Product product); bool IsAreaCoolingOperational(StorageArea area); } // 领域实体类 public class WarehouseBoxUnit { private readonly IWarehouseSafetyChecker _safetyChecker; public WarehouseBoxUnit(IWarehouseSafetyChecker safetyChecker) { _safetyChecker = safetyChecker; } public void AddProduct(Product product, Position pos) { // 调用抽象接口完成规则验证 if (!_safetyChecker.IsPositionSafeForProduct(pos, product)) { throw new InvalidOperationException("该货位不符合安全存放规则"); } if (product.IsFreshMeat && !_safetyChecker.IsAreaCoolingOperational(pos.Area)) { throw new InvalidOperationException("生鲜肉不能存放于空调故障区域"); } // 执行添加产品逻辑 // ... } }
- 基础设施层实现接口,负责实际的外部数据查询:
// 基础设施层实现 public class WarehouseSafetyChecker : IWarehouseSafetyChecker { private readonly IWarehouseRepository _repo; private readonly ISensorApiClient _sensorClient; public WarehouseSafetyChecker(IWarehouseRepository repo, ISensorApiClient sensorClient) { _repo = repo; _sensorClient = sensorClient; } public bool IsPositionSafeForProduct(Position targetPos, Product product) { var upperProduct = _repo.GetProductAtPosition(targetPos.UpperPos); var lowerProduct = _repo.GetProductAtPosition(targetPos.LowerPos); return !product.IsAcid || !(upperProduct?.IsAlkali ?? false || lowerProduct?.IsAlkali ?? false); } public bool IsAreaCoolingOperational(StorageArea area) { var sensorData = _sensorClient.GetCoolingStatus(area.Id); return sensorData.IsOperational; } }
这种方式严格遵循依赖倒置,领域层只依赖抽象,基础设施层依赖领域层接口,完全符合Clean Architecture分层规则,同时保证核心安全规则封装在领域层,不会因新增用例遗漏验证。
2. 规格模式(Specification Pattern)
将每个安全规则封装为独立的规格类,规格类依赖抽象数据源接口,实体或领域服务通过组合规格完成验证:
- 领域层规格类示例:
// 抽象规格基类 public abstract class Specification<T> { public abstract bool IsSatisfiedBy(T candidate); } // 酸碱分离规格 public class AcidAlkaliSeparationSpecification : Specification<(Product product, Position pos)> { private readonly IWarehouseRepository _repo; public AcidAlkaliSeparationSpecification(IWarehouseRepository repo) { _repo = repo; } public override bool IsSatisfiedBy((Product product, Position pos) candidate) { var (product, pos) = candidate; if (!product.IsAcid && !product.IsAlkali) return true; var upperProduct = _repo.GetProductAtPosition(pos.UpperPos); var lowerProduct = _repo.GetProductAtPosition(pos.LowerPos); return !(product.IsAcid && (upperProduct?.IsAlkali ?? false || lowerProduct?.IsAlkali ?? false)) && !(product.IsAlkali && (upperProduct?.IsAcid ?? false || lowerProduct?.IsAcid ?? false)); } } // 生鲜肉制冷规格 public class FreshMeatCoolingSpecification : Specification<(Product product, Position pos)> { private readonly ISensorApiClient _sensorClient; public FreshMeatCoolingSpecification(ISensorApiClient sensorClient) { _sensorClient = sensorClient; } public override bool IsSatisfiedBy((Product product, Position pos) candidate) { var (product, pos) = candidate; if (!product.IsFreshMeat) return true; var sensorData = _sensorClient.GetCoolingStatus(pos.Area.Id); return sensorData.IsOperational; } }
- 领域服务中组合规格验证:
public class WarehouseService { private readonly IEnumerable<Specification<(Product, Position)>> _safetySpecifications; public WarehouseService(IEnumerable<Specification<(Product, Position)>> safetySpecifications) { _safetySpecifications = safetySpecifications; } public void AddProductToBox(WarehouseBoxUnit box, Product product, Position pos) { var candidate = (product, pos); if (!_safetySpecifications.All(spec => spec.IsSatisfiedBy(candidate))) { throw new InvalidOperationException("违反仓库安全存放规则"); } box.AddProduct(product, pos); } }
规格模式的优势在于规则可复用、可组合,新增规则只需添加新的规格类,无需修改现有实体或服务,同时保证所有规则集中在领域层,避免遗漏。
3. 领域事件驱动验证
将验证逻辑通过领域事件触发,确保添加产品的操作必然触发验证:
- 领域层定义事件和实体:
// 领域事件 public record ProductAddingEvent(WarehouseBoxUnit Box, Product Product, Position TargetPos); // 领域实体 public class WarehouseBoxUnit { private readonly IDomainEventPublisher _eventPublisher; public WarehouseBoxUnit(IDomainEventPublisher eventPublisher) { _eventPublisher = eventPublisher; } public void AddProduct(Product product, Position pos) { // 发布添加前事件 _eventPublisher.Publish(new ProductAddingEvent(this, product, pos)); // 执行添加操作 // ... } }
- 领域服务订阅事件并执行验证:
public class ProductAddingValidationHandler : IDomainEventHandler<ProductAddingEvent> { private readonly IWarehouseSafetyChecker _safetyChecker; public ProductAddingValidationHandler(IWarehouseSafetyChecker safetyChecker) { _safetyChecker = safetyChecker; } public void Handle(ProductAddingEvent @event) { var (box, product, pos) = @event; if (!_safetyChecker.IsPositionSafeForProduct(pos, product)) { throw new InvalidOperationException("该货位不符合安全存放规则"); } if (product.IsFreshMeat && !_safetyChecker.IsAreaCoolingOperational(pos.Area)) { throw new InvalidOperationException("生鲜肉不能存放于空调故障区域"); } } }
需注意事件需同步处理,确保验证在产品添加完成前执行。这种方式适合验证逻辑复杂、需要跨领域协作的场景,同时保证只要执行添加操作就会触发验证,不会遗漏。
内容的提问来源于stack exchange,提问作者Klaus P.
相关产品推荐
相关产品推荐

