CQRS Validation with MediatR Pipeline and FluentValidation
Tags: aspnetcore, validation, middleware, mediatr
Published: 2023-10-14 10:54:53

---
title: CQRS Validation with MediatR Pipeline and FluentValidation
published: true
date: 2023-09-30 00:00:00 UTC
tags: aspnetcore,validation,middleware,mediatr
canonical_url: https://www.milanjovanovic.tech/blog/cqrs-validation-with-mediatr-pipeline-and-fluentvalidation
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vfsf3w0ybom4nqvqpy7.png
---
Validation is an essential cross-cutting concern that you need to solve in your application. You want to ensure the request is valid before you consider processing it.

Another important question you need to answer is how you approach different types of validation. For example, I consider input and business validation differently, and each deserves a specific solution.

I want to show you an elegant solution for validation using [MediatR](https://github.com/jbogard/MediatR) and [FluentValidation.](https://docs.fluentvalidation.net/en/latest/index.html)

If you aren't using [CQRS](https://learn.microsoft.com/en-us/azure/architecture/patterns/cqrs) with MediatR, don't worry. Everything I explain about validation can easily be adapted to other paradigms.

Here's what I'm going to talk about in this week's newsletter:

- Standard validation approach
- Input vs business validation
- Separating validation logic
- Generic `ValidationBehavior`

Let's dive in.

## The Standard Command Validation Approach

The standard way of implementing validation is right before processing the command. The validation is tightly coupled to the command handler, which could be problematic.

I find this approach difficult to maintain as the complexity of the validation increases. Each change to the validation logic also touches the handler, and the handler itself can grow out of control.

It also makes it harder to differentiate between input and _business_ validation.

Here's an example `ShipOrderCommandHandler` that checks if the `ShippingAddress.Country` is one of the supported countries:

```csharp
internal sealed record ShipOrderCommandHandler
    : IRequestHandler<ShipOrderCommand>
{
    private readonly IOrderRepository _orderRepository;
    private readonly IShippingService _shippingService;
    private readonly ShipmentSettings _shipmentSettings;

    public async Task Handle(
        ShipOrderCommand command,
        CancellationToken cancellationToken)
    {
        if (!_shipmentSettings
                .SupportedCountries
                .Contains(command.ShippingAddress.Country))
        {
            throw new ArgumentException(nameof(ShipOrderCommand.Address));
        }

        var order = _orderRepository.Get(command.OrderId);

        _shippingService.ShipTo(
            command.ShippingAddress,
            command.ShippingMethod);
    }
}
```

What if we can separate command validation and command handling?

## Input Validation and Business Validation

I mentioned input and _business_ validation in the previous section.

Here's how I consider them to be different:

- **Input validation** - We only validate that the command is _processable_. These are simple validations, such as checking for `null` values, empty strings, etc.
- **Business validation** - We validate the command to satisfy the business rules. This includes checking the system state for required preconditions before processing the command.

Another way to compare them is cheap vs. expensive. Input validation is usually cheap to execute and can be done in memory. While business validation involves reading state and is slower.

So, input validation sits at the entry point of the use case before handling the request. After it completes, we have a _valid_ command. And this is a rule I always follow - an invalid command should never reach the handler.

## Input Validation With FluentValidation

[FluentValidation](https://docs.fluentvalidation.net/en/latest/index.html) is an excellent validation library for .NET, which uses a fluent interface and lambda expressions for building strongly typed validation rules.

Here's the `ShipOrderCommand` that we want to validate:

```csharp
public sealed record ShipOrderCommand : IRequest
{
    public Guid OrderId { get; set; }

    public string ShippingMethod { get; set; }

    public Address ShippingAddress { get; set; }
}
```

To implement a validator with [FluentValidation,](https://github.com/FluentValidation/FluentValidation) you create a class that inherits from the `AbstractValidator<T>` base class. Then, you can add the validation rules from the constructor using `RuleFor`:

```csharp
public sealed record ShipOrderCommandValidator
    : AbstractValidator<ShipOrderCommand>
{
    public ShipOrderCommandValidator(ShipmentSettings settings)
    {
        RuleFor(command => command.OrderId)
            .NotEmpty()
            .WithMessage("The order identifier can't be empty.");

        RuleFor(command => command.ShippingMethod)
            .NotEmpty()
            .WithMessage("The shipping method can't be empty.");

        RuleFor(command => command.ShippingAddress)
            .NotNull()
            .WithMessage("The shipping address can't be empty.");

        RuleFor(command => command.ShippingAddress.Country)
            .Must(country => settings.SupportedCountries.Contains(country))
            .WithMessage("The shipping country isn't supported.");
    }
}
```

The naming convention I like to use is the name of the command and append _Validator_. You can also enforce this by writing [architecture tests.](https://www.milanjovanovic.tech/blog/enforcing-software-architecture-with-architecture-tests)

To automatically register all validators from an assembly, you need to call the `AddValidatorsFromAssembly` method:

```csharp
services.AddValidatorsFromAssembly(ApplicationAssembly.Assembly);
```

## Running Validation From the Use Case

To run the `ShipOrderCommandValidator`, you can use the `IValidator<T>` service and inject it from the constructor.

The validator exposes a few methods you can call, like `Validate`, `ValidateAsync`, or `ValidateAndThrow`.

The `Validate` method returns a `ValidationResult` object which contains two properties:

- `IsValid` - a boolean flag saying whether the validation succeeded
- `Errors` - a collection of `ValidationFailure` objects containing any validation failures

Alternatively, calling the `ValidateAndThrow` method throws a `ValidationException` if validation fails.

```csharp
internal sealed record ShipOrderCommandHandler
    : IRequestHandler<ShipOrderCommand>
{
    private readonly IOrderRepository _orderRepository;
    private readonly IShippingService _shippingService;
    private readonly IValidator<ShipOrderCommand> _validator;

    public async Task Handle(
        ShipOrderCommand command,
        CancellationToken cancellationToken)
    {
        _validator.ValidateAndThrow(command);

        var order = _orderRepository.Get(command.OrderId);

        _shippingService.ShipTo(
            command.ShippingAddress,
            command.ShippingMethod);
    }
}
```

This approach forces you to define an explicit dependency on `IValidator` in every command handler.

What if we can implement this cross-cutting concern in a more generic way?

## MediatR Validation Pipeline

Here's a complete implementation of a `ValidationBehavior` using FluentValidation and MediatR's `IPipelineBehavior`.

The `ValidationBehavior` acts as a middleware for the request pipeline and performs validation. If the validation fails, it will throw a custom `ValidationException` with a collection of `ValidationError` objects.

I also want to highlight the use of `ValidateAsync`, which allows you to define asynchronous validation rules. You must call the `ValidateAsync` method if you have asynchronous rules. Otherwise, the validator will throw an exception.

```csharp
public class ValidationBehavior<TRequest, TResponse>
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : ICommandBase
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
    {
        _validators = validators;
    }

    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken cancellationToken)
    {
        var context = new ValidationContext<TRequest>(request);

        var validationFailures = await Task.WhenAll(
            _validators.Select(validator => validator.ValidateAsync(context)));

        var errors = validationFailures
            .Where(validationResult => !validationResult.IsValid)
            .SelectMany(validationResult => validationResult.Errors)
            .Select(validationFailure => new ValidationError(
                validationFailure.PropertyName,
                validationFailure.ErrorMessage))
            .ToList();

        if (errors.Any())
        {
            throw new Exceptions.ValidationException(errors);
        }

        var response = await next();

        return response;
    }
}
```

Don't forget to register the `ValidationBehavior` with MediatR by calling `AddOpenBehavior`:

```csharp
services.AddMediatR(config =>
{
    config.RegisterServicesFromAssemblyContaining<ApplicationAssembly>();

    config.AddOpenBehavior(typeof(ValidationBehavior<,>));
});
```

## Handling Validation Exceptions

Here's a custom `ValidationExceptionHandlingMiddleware` middleware that only handles the custom `ValidationException`. It converts the exception to a `ProblemDetails` response and includes any validation errors.

You can easily expand this to be a generic global exception handler.

```csharp
public class ValidationExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;

    public ValidationExceptionHandlingMiddleware(RequestDelegate next)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exceptions.ValidationException exception)
        {
            var problemDetails = new ProblemDetails
            {
                Status = StatusCodes.Status400BadRequest,
                Type = "ValidationFailure",
                Title = "Validation error",
                Detail = "One or more validation errors has occurred"
            };

            if (exception.Errors is not null)
            {
                problemDetails.Extensions["errors"] = exception.Errors;
            }

            context.Response.StatusCode = StatusCodes.Status400BadRequest;

            await context.Response.WriteAsJsonAsync(problemDetails);
        }
    }
}
```

You also need to include the middleware in the request pipeline by calling `UseMiddleware`:

```csharp
app.UseMiddleware<ExceptionHandlingMiddleware>();
```

## Takeaway

This implementation of `ValidationBehavior` is something I use in real projects, and it works incredibly well. If I don't want to throw an exception, I can update the `ValidationBehavior` to return a result object instead.

How do you apply this if you're not using MediatR?

I'm using an `IPipelineBehavior`, which allows me to implement a _middleware_ wrapping each request.

So, all you need is a way to implement middleware and place your validation inside. And I like having options, so here are [three ways to create middleware in ASP.NET Core.](https://www.milanjovanovic.tech/blog/3-ways-to-create-middleware-in-asp-net-core)

Hope this was valuable.

Stay awesome!

* * *

**P.S. Whenever you're ready, there are 2 ways I can help you:**

1. [**Pragmatic Clean Architecture:**](https://www.milanjovanovic.tech/pragmatic-clean-architecture?utm_source=dev.to&utm_medium=website&utm_campaign=cross-posting) This comprehensive course will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture. [**Join 1,000+ students here.**](https://www.milanjovanovic.tech/pragmatic-clean-architecture?utm_source=dev.to&utm_medium=website&utm_campaign=cross-posting)

2. [**Patreon Community:**](https://www.patreon.com/milanjovanovic) Think like a senior software engineer with **access to the source code** I use in my YouTube videos and **exclusive discounts** for my courses. [**Join 860+ engineers here.**](https://www.patreon.com/milanjovanovic)