我想使用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
顶层?
答案 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/,请参阅原因有效命令失败以及如何处理