I was wondering where exactly we should put input validations(imagine an API call send input to apply free times of a user). Is it right to inject validation class in service Layer and call validate method inside service? or it's better to put it in the infrastructure layer or even in Domain model? I just wanted to see a sample code that's implement validation of input for an API in Domain-driven design approach? what if I use CQRS architecture?
问题:
回答1:
I use in my DDD/CQRS project following approach, structure of a project is API layer, Domain layer, Data Access layer, all input data from UI or from User are validated before, command are created and dispatched, to update the state of Domain, and we validate input data to times one is on the UI, (Angular app), and second one in Web API layer, if the data are valid the CQRS command are created and dispatched after that you can have Business logic validation. For validation you can use FastValidator or FluentValidation
UPDATE: Here is the simple example we have API for Create Batch Entity.
[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);
}
As you can see as user input data will be BatchEditModel
.
so we have BatchEditModelValidator
which contains input data validation:
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);
}
}
this Validator will be executed before BatchEditModel will be mapped to CreateBatchCommand
and in CreateBatchCommandHandler we have Bussines logic validation 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;
}
回答2:
what if I use CQRS architecture?
I wouldn't expect CQRS to change things very much.
Usually, by the time you are invoking a method in a domain entity, your inputs should have already been converted from their domain agnostic form into value objects.
Value objects are expected to be constructed in a valid state, and often include a check of a constraint within the constructor/factory method that produces it. However, in Java and similar languages, the implementation of the constructor usually throws (because constructors don't have any other way of reporting a problem).
Often what clients want instead is a clear understanding of all of the constraints violated by the input data, rather than just the first one. So you may need to pull the constraints out as first class citizens in the model, as predicates that can be checked.
回答3:
My approach is putting validation in the domain model, I validate the functionality of aggregates, entities, value objects, etc.
Then you can validate application services too, and user interface too. But those validations are a plus, a validation enhancement from the user point of view, as validation is faster.
Why this duplication of validations at different layers? Well, because if you just rely on UI or application service validations, it maybe possible that if they don't work well for whatever reason, and you don't validate the domain model, you are executing domain functionality without validating it.
Also, I would point out that not all the validations can be done at UI or at application layer, because you may have to access domain.
Finally, doing CQRS or not is independent on where you decide to put the validations. It's just that if you do CQRS, then validations at application layer are easier to do, as you can put them in decorators that wrap commands and queries.
Hope my explanation helps.
回答4:
You should validate in your app service before attempting to modify your domain. Validation should be towards the edges of your app (but not in the UI) so invalid or incomplete requests aren't even getting into your domain model.
I consider it two levels of validation because you will validate the request before attempting some behavior on the model then the model should again verify for internal consistency, since it can never be persisted in an invalid state.
回答5:
where should put input validation [in Domain Driven Design]?
This is largely unrelated to DDD, but: the closest possible to the input source.
You aren't going to wait until invalid data has crossed 4 layers to discard it.
Input validation precisely means you don't need anything else (e.g. loading other data) to check it, so you might as well do it as soon as you can. Of course, caveats apply, like any validation that can be circumvented must be double checked - client side javascript for instance.