我想知道我们应该在哪里放置输入验证(想象一下API调用发送输入以应用用户的空闲时间)。在服务层中注入验证类并在服务中调用validate方法是否正确?或者最好将它放在基础设施层甚至是域模型中?我只是想看一个示例代码,它实现了域驱动设计方法中API的输入验证?如果我使用CQRS架构怎么办?
我在我的DDD / CQRS项目中使用以下方法,项目的结构是API层,域层,数据访问层,来自UI或来自用户的所有输入数据都经过验证,命令被创建和分派,以更新域的状态,我们验证输入数据到UI上的时间(Angular app)和Web API层中的第二个,如果数据有效,则创建CQRS命令并调度之后可以进行业务逻辑验证。要进行验证,您可以使用FastValidator或FluentValidation
更新:以下是我们为创建批处理实体提供API的简单示例。
[HttpPost]
[Route("create")]
public IHttpActionResult Create([FromBody] BatchEditModel model)
{
var createCommand = model.Map<BatchEditModel, CreateBatchCommand>();
var result = (OperationResult<int>) _commandDispatcher.Dispatch(createCommand);
return Result(result);
}
正如您所看到的那样,用户输入数据将是BatchEditModel
。
所以我们有BatchEditModelValidator
,其中包含输入数据验证:
public class BatchEditModelValidator : AbstractValidator<BatchEditModel>
{
public BatchEditModelValidator()
{
RuleFor(x => x.Number).NotEmpty()
.WithMessage(ValidatorMessages.MustBeSpecified);
RuleFor(x => x.ClientId).GreaterThan(0)
.WithMessage(ValidatorMessages.MustBeSpecified);
RuleFor(x => x.EntryAssigneeId).GreaterThan(0)
.WithMessage(ValidatorMessages.MustBeSpecified);
RuleFor(x => x.ReviewAssigneeId).GreaterThan(0)
.WithMessage(ValidatorMessages.MustBeSpecified);
RuleFor(x => x.Description).NotEmpty()
.WithMessage(ValidatorMessages.MustBeSpecified);
}
}
在将BatchEditModel映射到CreateBatchCommand之前,将执行此Validator
在CreateBatchCommandHandler
,我们有业务逻辑验证CheckUniqueNumber
public OperationResult Handle(CreateBatchCommand command)
{
var result = new OperationResult<int>();
if (CheckUniqueNumber(result, command.ClientId, command.Number))
{
if (result.IsValid)
{
var batch = _batchFactory.Create(command);
_batchRepository.Add(batch);
_batchRepository.Save();
result.Value = batch.Id;
}
}
return result;
}
如果我使用CQRS架构怎么办?
我不希望CQRS改变很多东西。
通常,当您在域实体中调用方法时,您的输入应该已经从其域不可知形式转换为值对象。
值对象应该在有效状态下构造,并且通常包括在生成它的构造函数/工厂方法中检查约束。但是,在Java和类似语言中,构造函数的实现通常会抛出(因为构造函数没有任何其他报告问题的方法)。
通常客户想要的是清楚地理解输入数据违反的所有约束,而不仅仅是第一个。因此,您可能需要将约束作为模型中的第一类公民,作为可以检查的谓词。
我的方法是在域模型中进行验证,我验证聚合,实体,值对象等的功能。
然后您也可以验证应用程序服务以及用户界面。但是这些验证是一个优点,从用户的角度来看是一种验证增强,因为验证速度更快。
为什么在不同层重复验证?好吧,因为如果您只是依赖于UI或应用程序服务验证,那么如果它们由于某种原因无法正常工作,并且您不验证域模型,则可能会执行域功能而不验证它。
此外,我要指出的是,并非所有验证都可以在UI或应用程序层完成,因为您可能必须访问域。
最后,做CQRS与否取决于您决定放置验证的位置。只是如果你做CQRS,那么应用层的验证就更容易了,因为你可以将它们放在包装命令和查询的装饰器中。
希望我的解释有所帮助
在尝试修改域名之前,您应该在应用服务中进行验证。验证应该是您的应用程序的边缘(但不是在UI中),因此无效或不完整的请求甚至不会进入您的域模型。
我认为它是两级验证,因为您将在尝试模型上的某些行为之前验证请求,然后模型应再次验证内部一致性,因为它永远不会保持在无效状态。
应该在哪里放置输入验证[在域驱动设计中]?
这与DDD基本无关,但是:最接近输入源的可能性。
你不会等到无效数据越过4层才能丢弃它。
输入验证精确意味着您不需要任何其他内容(例如加载其他数据)来检查它,因此您可以尽快执行此操作。当然,警告适用,就像任何可以规避的验证必须经过双重检查 - 例如客户端javascript。