dotnet-cqrs/.claude/rules/validation.md
Mathias Beaulieu-Duncan a4525bad6a Add Claude Code harness: rules, skills, hooks, and editorconfig
- Add path-specific rules for commands/queries, dynamic queries, validation, and gRPC
- Add /add-command, /add-query, /add-dynamic-query scaffolding skills
- Add project settings with post-edit formatting, proto validation, and build-gate hooks
- Add .editorconfig codifying existing code style conventions
- Trim CLAUDE.md from 414 to 130 lines (domain details moved to rules)
- Add .harness-version tracking for the shared claude-harness repo

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 03:30:27 -04:00

1.9 KiB

paths
*Validator*.cs
*Validation*

FluentValidation Integration

Validator Pattern

Validators live in the same file as their command/query, named {CommandName}Validator:

public class PlaceOrderCommandValidator : AbstractValidator<PlaceOrderCommand>
{
    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:

// Command without result + validator
services.AddCommand<DeactivateAccountCommand, DeactivateAccountCommandHandler, DeactivateAccountCommandValidator>();

// Command with result + validator
services.AddCommand<PlaceOrderCommand, int, PlaceOrderCommandHandler, PlaceOrderCommandValidator>();

// Query + validator
services.AddQuery<FetchOrderByIdQuery, Order, FetchOrderByIdQueryHandler, FetchOrderByIdQueryValidator>();

These come from Svrnty.CQRS.FluentValidation.ServiceCollectionExtensions — they call the base AddCommand/AddQuery then register IValidator<T>.

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<T>, not IValidator<T> directly
  • Define all rules in the constructor
  • Always include .WithMessage() for user-facing error messages
  • Validator constructor can inject services for async/database validation