где следует поставить проверку входных данных в домене управляемом дизайне - PullRequest
0 голосов
/ 11 октября 2018

Мне было интересно, куда именно мы должны поместить проверки ввода (представьте, что вызов API отправляет ввод, чтобы применить свободное время пользователя).Правильно ли внедрить класс валидации в сервисный уровень и вызвать метод валидации внутри сервиса?или лучше поместить его на уровень инфраструктуры или даже в модель предметной области?Я просто хотел увидеть пример кода, который реализует проверку ввода для API в подходе проектирования на основе домена?что если я использую архитектуру CQRS?

Ответы [ 5 ]

0 голосов
/ 15 октября 2018

, куда следует поместить проверку входных данных [в дизайне, управляемом доменом]?

Это в основном не связано с DDD, но: максимально близко к источнику входного сигнала.

Вы не будете ждать, пока недействительные данные пересекут 4 слоя, чтобы отбросить их.

Проверка ввода точно означает, что вам больше ничего не нужно (например, загружать другие данные) для проверкитак что вы можете сделать это как можно скорее.Конечно, применяются предостережения, как и любая проверка, которая может быть обойдена, должна быть дважды проверена - например, JavaScript на стороне клиента.

0 голосов
/ 13 октября 2018

Прежде чем пытаться изменить свой домен, вы должны проверить его в службе приложений.Проверка должна быть ближе к краям вашего приложения (но не в пользовательском интерфейсе), поэтому недействительные или неполные запросы даже не попадают в модель вашего домена.

Я считаю, что это два уровня проверки, потому что вы будете проверять запрос, прежде чем пытаться выполнить какое-либо поведение в модели, затем модель должна снова проверить внутреннюю согласованность, поскольку она никогда не может быть сохранена в недопустимом состоянии.

0 голосов
/ 11 октября 2018

что если я использую архитектуру CQRS?

Я бы не ожидал, что CQRS сильно изменит ситуацию.

Обычно, когда вы вызываете метод в объекте домена, ваши входные данные уже должны были быть преобразованы изих доменная агностическая форма превращается в объекты ценности.

Ожидается, что объекты-значения будут созданы в допустимом состоянии и часто включают проверку ограничения в методе конструктора / фабрики, который его создает.Однако в Java и аналогичных языках реализация конструктора обычно вызывает (поскольку у конструкторов нет другого способа сообщить о проблеме).

Зачастую клиенты хотят получить четкое представление обо всехограничения нарушаются входными данными, а не только первыми.Поэтому вам, возможно, потребуется снять ограничения в качестве первоклассных граждан в модели в качестве предикатов, которые можно проверить.

0 голосов
/ 11 октября 2018

Мой подход - поставить валидацию в модели предметной области, я проверяю функциональность агрегатов, сущностей, объектов значений и т. Д.

Затем вы также можете проверить сервисы приложений и пользовательский интерфейс.Но эти проверки являются плюсом, улучшением проверки с точки зрения пользователя, поскольку проверка выполняется быстрее.

Почему это дублирование проверок на разных уровнях?Ну, потому что, если вы просто полагаетесь на проверки пользовательского интерфейса или службы приложений, возможно, что если они по какой-то причине не работают должным образом и вы не проверяете модель домена, вы выполняете функциональность домена, не проверяя ее.

Кроме того, я хотел бы отметить, что не все проверки могут быть выполнены в пользовательском интерфейсе или на прикладном уровне, поскольку вам может потребоваться доступ к домену.

Наконец, выполнение CQRS или нет зависит от того, где вырешите поставить проверки.Просто если вы делаете CQRS, тогда проверки на прикладном уровне легче выполнять, так как вы можете поместить их в декораторы, которые обертывают команды и запросы.

Надеюсь, мое объяснение поможет.

0 голосов
/ 11 октября 2018

Я использую в своем проекте DDD / CQRS следующий подход, структура проекта - уровень API, уровень домена, уровень доступа к данным, все входные данные из пользовательского интерфейса или от пользователя проверяются до того, как команда создается и отправляется, чтобы обновитьсостояние домена, и мы проверяем входные данные, когда один из них находится на пользовательском интерфейсе (приложение Angular), а второй - на уровне веб-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 у нас есть проверка бизнес-логики 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;
}
...