实现业务规则的规范模式

时间:2014-05-06 07:56:48

标签: c# exception domain-driven-design cqrs specifications

我想使用Specification在我的N-Layerd DDD应用程序中应用业务规则。 我在CQRS也使用了Application Layer模式。 所以我在Interface中定义了Domain

public interface ISpecification<T>
{
    Expression<Func<T, bool>> Predicate { get; }
    bool IsSatisfiedBy(T entity);
}

以及实现上述界面的一些Specification,例如BigOrderSpecification : ISpecification<Order>SpecialOrderSpecification:ISpecification<Order>

在我的OrderProcessCommandHandler课程中,我使用了这些Specification s:

public class OrderProcessCommandHandler : ICommandHandler<Order>
{
    OrderCommand _command;
    public OrderProcessCommandHandler(OrderCommand command) 
    {
       _command = command;
    }
    public Handle()
    {
        var bigOrderSpec = new BigOrderSpecification();
        var specialOrderSpec = new SpecialOrderSpecification();
        var spec = bigOrderSpec.And(specialOrderSpec);
        if (spec.IsSatisfiedBy(_commnand.Order))
           // do some things
        else
            throw new BusinessException("Some business rules violated.") 
    }      
}

如您所见,如果在订单处理期间,一个或多个规范不满足,我无法将BusinessException 明确消息投放到顶层,只有

  

违反了一些商业规则。

我如何创建包含所有BR违规原因的明确按摩并将其传递给我的BusinessException顶层?

4 个答案:

答案 0 :(得分:3)

使用从规范引出并在命令处理程序中处理的事件。

public class BusinessRuleFailure : EventArgs
{
    public BusinessRuleFailure(string reason)
    {
        Reason = reason;
    }

    public string Reason { get; private set; }
}

public delegate void BusinessRuleFailureHandler(BusinessRuleFailure failure);

public interface ISpecification<T>
{
    event BusinessRuleFailureHandler NotSatisified;

    Expression<Func<T, bool>> Predicate { get; }
    bool IsSatisfiedBy(T entity);
}

public class OrderProcessCommandHandler : ICommandHandler<Order>
{
    OrderCommand _command;
    public OrderProcessCommandHandler(OrderCommand command) 
    {
       _command = command;
    }
    public Handle()
    {
        List<string> failures = new List<string>();

        var bigOrderSpec = new BigOrderSpecification();
        var specialOrderSpec = new SpecialOrderSpecification();

        bigOrderSpec.NotSatisified += failure => failures.Add(failure.Reason);
        specialOrderSpec.NotSatisified += failure => failures.Add(failure.Reason);

        var spec = bigOrderSpec.And(specialOrderSpec);
        if (spec.IsSatisfiedBy(_commnand.Order))
           // do some things
        else
        throw new BusinessException("Some business rules violated.", failures);
    } 
}

答案 1 :(得分:1)

几天前受到评论的启发。变化

bool IsSatisfiedBy(T entity);

Result IsSatisfiedBy(T entity);

public class Result 
{
    public boolean IsSatisfied{}
    public List<String> message() {}
}

但你必须实施&amp;&amp; ,!和|| :

&& Result r1 = spec1.satisfied(o);
    if (r1.isSatisfied()) {
        Result r2 = spec2.satisfied(o);
        if (r2.isSatisfied()) {
            return new Result();
        } else {
            return r2;
        }
    } else {
        return r1;
    }

 || Result r1 = spec1.satisfied(o);
    if (r1.isSatisfied()) {
        return new Res();           
    } else {
        Result r2 = spec2.satisfied(o);
        if (r2.isSatisfied()) {
            return new Result();
        } else {
            return r2.append(r1.message());
        }
    }

答案 2 :(得分:0)

向ISpecification接口添加一个事件,该接口返回一个字符串(或包含该字符串的对象)。对于每个业务规则,如果不满足,则触发包含原因的事件。在命令处理程序中,监听每个规范的事件(在循环中)并将字符串收集到集合中,如果没有满足规则,则使用您的错误集合抛出异常。

答案 3 :(得分:0)

您可以考虑在调用命令处理程序之前单独验证。让命令解雇并忘记。先验证然后发送到业务层,假设它可以工作。有一个命令验证处理程序,返回例如IEnumerable<IErrorMessage>

失败时,域只是抛出而不是在UI层中处理它,所以不需要一直传回它。以其他方式处理,例如发送带有异常的电子邮件或引发一个事件,该事件将在稍后的某个时间点被异步并显示在UI中。

在域中,它是否有可能正确验证,然后在验证和执行之间发生了什么?它归结为告知用户某些错误的商业价值。

此Udi Dahan视频涵盖了https://skillsmatter.com/skillscasts/1250-udi-dahan-command-query-responsibility-segregation

这一概念

此博客文章还涵盖了http://www.udidahan.com/2009/12/09/clarified-cqrs/,请参阅原因有效命令失败以及如何处理