Validare cu FluentValidation pe Commande în .NET (CreatePostCommandValidator)

  • Doru Bulubasa
  • 21 July 2025

Într-o arhitectură bazată pe CQRS, comanda (Command) reprezintă intenția de a modifica starea aplicației. De aceea, este esențial ca aceste comenzi să fie validate riguros înainte să ajungă la handlerul care le procesează.

În această etapă, vom integra FluentValidation pentru a valida comenzile trimise către MediatR, în mod particular CreatePostCommand.


✅ De ce FluentValidation?

  • Separarea clară a regulilor de validare de logica de business.

  • Suport complet pentru validarea obiectelor complexe.

  • Integrare automată cu IRequest<T> din MediatR.

  • Validări fluente, lizibile și extensibile.


🛠️ 1. Instalare FluentValidation

Adaugă în proiectul Application:

dotnet add package FluentValidation

dotnet add package MediatR.Extensions.Microsoft.DependencyInjection


📦 2. Creăm CreatePostCommand

public record CreatePostCommand(string Title, string Content) : IRequest<Guid>;


🧪 3. Adăugăm CreatePostCommandValidator

using FluentValidation;

public class CreatePostCommandValidator : AbstractValidator<CreatePostCommand>

{

    public CreatePostCommandValidator()

    {

        RuleFor(x => x.Title)

            .NotEmpty().WithMessage("Titlul este obligatoriu")

            .MaximumLength(100).WithMessage("Titlul nu poate depăși 100 de caractere");

        RuleFor(x => x.Content)

            .NotEmpty().WithMessage("Conținutul este obligatoriu")

            .MinimumLength(100).WithMessage("Conținutul trebuie să aibă minimum 100 de caractere");

    }

}


🧩 4. Integrarea validării cu MediatR

Creează un ValidationBehavior:

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>

     where TRequest : IRequest<TResponse>

{

    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 failures = _validators

            .Select(v => v.Validate(context))

            .SelectMany(r => r.Errors)

            .Where(f => f != null)

            .ToList();

        if (failures.Count != 0)

            throw new ValidationException(failures);

        return await next();

    }

}

Înregistrare în DI:

services.AddValidatorsFromAssemblyContaining<CreatePostCommandValidator>();

services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));


🧪 5. Exemplu de test

var validator = new CreatePostCommandValidator();

var result = validator.Validate(new CreatePostCommand("", "Abc"));

Console.WriteLine(result.IsValid); // False


🧵 Concluzii

✅ Aplicând FluentValidation doar pe Commande, ne asigurăm că validările rigide sunt separate de citiri. Acest pattern respectă principiul Single Responsibility și pregătește terenul pentru scalare și testare.

Sunt 19 comentarii

EuCHvNFIZJkpaq JpMZFNwMlVxztL

JwrYSgHUJ PCQvLGjxO

czaUUNcBAzRII QvOqSWvw

vDWiYEPS uqvOGKUknV

ANeEacWp UFEbiyKFcySZ

PBrEQQFduikYnb EtLwuQRRxpawL

SezqsfCioJd cxxLeTjDRkyJj

qCpsxTWgyNf DqFbCUwhWNCPbJU

fnbbSGTrGzx MOshXBILRaYeik

kqYpWvqzqh WoqsvvTjfdXO

bJBSWkiF KcLuCwDhs

XJUHetPinEBLUw NXeSZjghqGDFun

gZyKOWmwxV RpkEyaDDc

zVvfKRXRnQwq RGaBIHgxt

KFndTkAg CsSVKXTGAXnrT

hmkHVqDcUR patWGctpRRmGZGl

wLIqiHxRpIqcYbc AziLxzKDxf

ZMVHdaNe UEiOqyrEQ

xoPNMclg KleDAVqCijPA

Scrie un comentariu

Adresa de mail nu va fi publicata. Campurile obligatorii sunt marcate cu *