我想知道我们应该在什么地方进行输入验证(想象一个API调用发送输入以应用用户的空闲时间)。在服务层中注入验证类并在服务内部调用validate方法是否正确?还是最好将其放入基础结构层甚至是Domain模型中?我只想看一个示例代码,该代码在域驱动的设计方法中实现对API输入的验证?如果我使用CQRS架构怎么办?
答案 0 :(得分:1)
我在DDD / CQRS项目中使用以下方法,一个项目的结构是API层,域层,数据访问层,所有来自UI或用户的输入数据都经过验证, 创建并分派命令以更新Domain的状态,并验证输入数据的时间,一次是在UI(Angular应用)上,第二次是在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之前执行
,在CreateBatchCommandHandler中,我们进行了Bussines逻辑验证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;
}
答案 1 :(得分:1)
如果我使用CQRS架构该怎么办?
我不希望CQRS会有很大改变。
通常,当您在域实体中调用方法时,您的输入应该已经从其与域无关的形式转换为值对象。
值对象应该在有效状态下构造,并且通常会在产生约束的构造函数/工厂方法中包括对约束的检查。但是,在Java和类似语言中,通常会抛出构造函数的实现(因为构造函数没有其他报告问题的方式)。
通常,客户想要的是对输入数据所违反的所有约束的清楚了解,而不仅仅是第一个约束。因此,您可能需要将约束作为模型中的一等公民淘汰,作为可以检查的谓词。
答案 2 :(得分:1)
我的方法是将验证放在域模型中,我验证集合,实体,值对象等的功能。
然后,您也可以验证应用程序服务和用户界面。但是这些验证是一个加号,从用户的角度来看,验证增强,因为验证更快。
为什么在不同的层重复进行验证?好吧,因为如果您仅依赖UI或应用程序服务验证,则可能由于某种原因它们不能很好地工作,并且您没有验证域模型,那么您在执行域功能时将不对其进行验证。>
此外,我要指出的是,并非所有验证都可以在UI或应用程序层进行,因为您可能必须访问域。
最后,是否进行CQRS取决于您决定放置验证的位置。只是,如果您执行CQRS,则可以在应用程序层进行验证,因为您可以将其放入包装命令和查询的装饰器中。
希望我的解释有所帮助。
答案 3 :(得分:1)
您应先在应用服务中进行验证,然后再尝试修改您的域。验证应该针对您应用的边缘(而不是用户界面),这样无效或不完整的请求甚至都不会进入您的域模型。
我认为它是两个验证级别,因为您将在对模型进行某些行为之前先验证请求,然后模型应再次验证内部一致性,因为它永远无法保持无效状态。
答案 4 :(得分:1)
应在[域驱动设计]中将输入验证放在哪里?
这在很大程度上与DDD无关,但是:与输入源最接近。
您不会等到无效数据越过4层将其丢弃。
输入验证恰好意味着您不需要其他任何内容(例如,加载其他数据)来进行检查,因此您最好尽快进行验证。当然,需要注意的是,就像任何可以规避的验证都必须仔细检查-例如客户端javascript。