应该在域驱动设计中放置输入验证?

问题描述 投票:1回答:5

我想知道我们应该在哪里放置输入验证(想象一下API调用发送输入以应用用户的空闲时间)。在服务层中注入验证类并在服务中调用validate方法是否正确?或者最好将它放在基础设施层甚至是域模型中?我只是想看一个示例代码,它实现了域驱动设计方法中API的输入验证?如果我使用CQRS架构怎么办?

validation domain-driven-design cqrs
5个回答
1
投票

我在我的DDD / CQRS项目中使用以下方法,项目的结构是API层,域层,数据访问层,来自UI或来自用户的所有输入数据都经过验证,命令被创建和分派,以更新域的状态,我们验证输入数据到UI上的时间(Angular app)和Web API层中的第二个,如果数据有效,则创建CQRS命令并调度之后可以进行业务逻辑验证。要进行验证,您可以使用FastValidatorFluentValidation

更新:以下是我们为创建批处理实体提供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;
}

1
投票

如果我使用CQRS架构怎么办?

我不希望CQRS改变很多东西。

通常,当您在域实体中调用方法时,您的输入应该已经从其域不可知形式转换为值对象。

值对象应该在有效状态下构造,并且通常包括在生成它的构造函数/工厂方法中检查约束。但是,在Java和类似语言中,构造函数的实现通常会抛出(因为构造函数没有任何其他报告问题的方法)。

通常客户想要的是清楚地理解输入数据违反的所有约束,而不仅仅是第一个。因此,您可能需要将约束作为模型中的第一类公民,作为可以检查的谓词。


1
投票

我的方法是在域模型中进行验证,我验证聚合,实体,值对象等的功能。

然后您也可以验证应用程序服务以及用户界面。但是这些验证是一个优点,从用户的角度来看是一种验证增强,因为验证速度更快。

为什么在不同层重复验证?好吧,因为如果您只是依赖于UI或应用程序服务验证,那么如果它们由于某种原因无法正常工作,并且您不验证域模型,则可能会执行域功能而不验证它。

此外,我要指出的是,并非所有验证都可以在UI或应用程序层完成,因为您可能必须访问域。

最后,做CQRS与否取决于您决定放置验证的位置。只是如果你做CQRS,那么应用层的验证就更容易了,因为你可以将它们放在包装命令和查询的装饰器中。

希望我的解释有所帮助


1
投票

在尝试修改域名之前,您应该在应用服务中进行验证。验证应该是您的应用程序的边缘(但不是在UI中),因此无效或不完整的请求甚至不会进入您的域模型。

我认为它是两级验证,因为您将在尝试模型上的某些行为之前验证请求,然后模型应再次验证内部一致性,因为它永远不会保持在无效状态。


1
投票

应该在哪里放置输入验证[在域驱动设计中]?

这与DDD基本无关,但是:最接近输入源的可能性。

你不会等到无效数据越过4层才能丢弃它。

输入验证精确意味着您不需要任何其他内容(例如加载其他数据)来检查它,因此您可以尽快执行此操作。当然,警告适用,就像任何可以规避的验证必须经过双重检查 - 例如客户端javascript。

© www.soinside.com 2019 - 2024. All rights reserved.