You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 18:03:18