NibblyPig
NibblyPig

Reputation: 52952

Where should the validation go in CQRS with MVC?

This is my post method for creating a new user:

[HttpPost]
public ActionResult CreateUser(CreateUserViewModel createUserViewModel)
{
    CreateSystemUserCommand createSystemUserCommand = new CreateSystemUserCommand()
    {
        Firstname = createUserViewModel.Forename,
        Surname = createUserViewModel.Surname,
        Username = createUserViewModel.Username,
        Password = createUserViewModel.Password
    };

    CreateSystemUserCommandHandler handler = new CreateSystemUserCommandHandler();

    handler.Execute(createSystemUserCommand);

    return RedirectToAction("ViewUsers");
}

There is some validation on the view model already, required fields etc. so the UI will have validation on it.

However I'm wondering how to do it server side.

Should I create a method createSystemUserCommand.Validate();

or before handler.Execute(), do handler.Validate()?

And how should I translate those errors into the ModelState? I'm guessing CQRS is not connected with MVC therefore it'd make no sense to return specifically model errors.

Any thoughts welcome on this. My gut feeling is to do handler.Validate since it'll keep validation logic within a single class, and it feels right, but I am open to suggestions.

Upvotes: 2

Views: 1556

Answers (3)

L-Four
L-Four

Reputation: 13541

I typically use FluentValidation in my application layer (like in the command handlers) and in domain layer. These validators all throw exceptions, which I catch in a global exception handler, which has the responsability to propagate them to the consumer in the correct format (for example as a fault in WCF). These messages are already in the correct language, based on the culture that was set on the thread (if you have multi lingual site).

On the site, the list of errors is then used. The error messages are simply displayed and based on the error keys I can add additional logic to disable controls etc.

So in my case validation is in most cases server side and only defined once in application and domain layer. On client side there can be some other small input validation, to restrict user input for example.

Upvotes: 4

Mrchief
Mrchief

Reputation: 76228

There are 2 types of validation here that you could potentially need:

  • One is simple ModelState validation which ensures that required fields are not missing, int is an int and so on. For that, using Data annotation attributes will do the trick.

  • The second type is business logic validation - something that may require accessing database or running some other validation logic to make sure that data integrity is not affected. That type of validation would be at the command level. The best way to do that is to follow the decorator pattern - wrap your actual handler in a validating handler:

    public class ValidationCommandHandlerDecorator<TCommand, TResult>
        : ICommandHandler<TCommand, TResult>
        where TCommand : ICommand<TResult>
    {
        private readonly ICommandHandler<TCommand, TResult> decorated;
    
        public ValidationCommandHandlerDecorator(ICommandHandler<TCommand, TResult> decorated)
        {
            this.decorated = decorated;
        }
    
        [DebuggerStepThrough]
        public TResult Handle(TCommand command)
        {
            var validationContext = new ValidationContext(command, null, null);
            Validator.ValidateObject(command, validationContext, validateAllProperties: true);
    
            return this.decorated.Handle(command);
        }
    }
    

    An example of validator would be:

    public class SomeCustomLogicValidator : IValidator {
    
        void IValidator.ValidateObject(object instance) {
            var context = new ValidationContext(instance, null, null);
    
            // Throws an exception when instance is invalid.
            Validator.ValidateObject(instance, context, validateAllProperties: true);
        }
    }       
    

    And then register it as:

    // using SimpleInjector.Extensions;
    container.RegisterDecorator(
    typeof(ICommandHandler<>),
    typeof(ValidationCommandHandlerDecorator<>));
    

    You can wrap as many decorators as you wish or even make it specific to a predicate (exact syntax depends on what DI framework you use):

    // another decorator
    container.RegisterDecorator(
    typeof(ICommandHandler<>),
    typeof(TransactionCommandHandlerDecorator<>));
    
    // specific decorator
    container.RegisterDecorator(
    typeof(ICommandHandler<>),
    typeof(AccessValidationCommandHandlerDecorator<>),
    context => !context.ImplementationType.Namespace.EndsWith("Admins"));
    

The example I have uses a DI framework which makes things simpler, but this idea can be extended without using any DI container as well.

Upvotes: 6

Yury Kovshov
Yury Kovshov

Reputation: 21

I'm not sure if you are using Data Annotations or not, but with Data Annotations it can be like this. Also see additional attribute ValidateAntiForgeryToken (may be useful for you).

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateUser(CreateUserViewModel createUserViewModel)
{
    if (ModelState.IsValid)
    {
        CreateSystemUserCommand createSystemUserCommand = new CreateSystemUserCommand()
        {
            Firstname = createUserViewModel.Forename,
            Surname = createUserViewModel.Surname,
            Username = createUserViewModel.Username,
            Password = createUserViewModel.Password
        };

        CreateSystemUserCommandHandler handler = new CreateSystemUserCommandHandler();

        handler.Execute(createSystemUserCommand);

        return RedirectToAction("ViewUsers");
    }

    return View(createUserViewModel);
}

But if you need complex validation you can go with:

if (ModelState.IsValid && handler.Validate())

Or you can implement you own validation logic and then add errors to ModelState by using ModelState.AddModelError.

Upvotes: 2

Related Questions