--- paths: - "*Validator*.cs" - "*Validation*" --- # FluentValidation Integration ## Validator Pattern Validators live in the **same file** as their command/query, named `{CommandName}Validator`: ```csharp public class PlaceOrderCommandValidator : AbstractValidator { public PlaceOrderCommandValidator() { RuleFor(x => x.CustomerId) .NotEmpty() .WithMessage("Customer is required"); RuleFor(x => x.Items) .NotEmpty() .WithMessage("At least one item is required"); } } ``` ## Registration Validators are registered alongside their handler using the FluentValidation overloads: ```csharp // Command without result + validator services.AddCommand(); // Command with result + validator services.AddCommand(); // Query + validator services.AddQuery(); ``` These come from `Svrnty.CQRS.FluentValidation.ServiceCollectionExtensions` — they call the base `AddCommand`/`AddQuery` then register `IValidator`. ## Error Formats by Protocol The framework handles validation execution automatically. Errors are returned as: - **HTTP (Minimal API)**: RFC 7807 Problem Details — `400 Bad Request` with structured error body - **gRPC**: Google Rich Error Model — `google.rpc.Status` with `BadRequest` detail containing `FieldViolations` You do NOT need to manually invoke validation in handlers. The endpoint layer (Minimal API or gRPC source-generated service) handles it. ## Rules - Inherit from `AbstractValidator`, not `IValidator` directly - Define all rules in the constructor - Always include `.WithMessage()` for user-facing error messages - Validator constructor can inject services for async/database validation