Skip to content

ADR-006: FluentValidation — Business Validation Framework

Date: 2026-02-24 Authors: Jean-Francois Meyers Scope: granit-dotnet (Granit.Validation, Granit.Wolverine)

The platform requires a validation framework for:

  • Business validation: complex and composable rules (address, SIRET, IBAN, email, locale) with standardized error codes
  • Wolverine integration: automatic command validation before execution via the middleware pipeline (WolverineFx.FluentValidation)
  • Error codes: mapping to RFC 7807 ProblemDetails for HTTP responses
  • Extensibility: custom reusable validators across modules

FluentValidation as the business validation framework.

  • License: Apache-2.0
  • Advantage: composable fluent API (RuleFor(x => x.Email).EmailAddress()), native Wolverine integration, large community, easy custom validators
  • Maturity: 15+ years, de facto standard for .NET validation
  • Advantage: native .NET, zero dependency, integrated with model binding
  • Disadvantage: limited to simple validations (attributes), no composition, no complex conditional validation, no Wolverine middleware integration, non-standardized error codes
  • License: MIT
  • Advantage: lightweight, based on DataAnnotations with extensions
  • Disadvantage: no composable rules, no Wolverine integration, limited community, does not cover complex business cases

Option 4: Custom validation (no framework)

Section titled “Option 4: Custom validation (no framework)”
  • Advantage: full control, no dependency
  • Disadvantage: considerable development and maintenance effort, reinventing the wheel, no standards, no middleware pipeline
CriterionFluentValidationDataAnnotationsMiniValidationCustom
LicenseApache-2.0Native .NETMITN/A
Composable rulesYesNoNoManual
Wolverine middlewareNativeNoNoManual
Conditional validationYes (When/Unless)NoNoManual
Error codesYes (WithErrorCode)LimitedLimitedManual
CommunityVery largeStandardLowN/A
RFC 7807 mappingVia Granit.AspNetCoreManualManualManual
  • Declarative and readable validation in each module
  • Wolverine integration: commands are validated before execution (DLQ on failure)
  • Standardized Granit error codes (e.g. VALIDATION.EMAIL.INVALID)
  • Reusable validators across packages (AddressValidator, SiretValidator)
  • Automatic mapping to ProblemDetails RFC 7807 via GranitExceptionHandler
  • Third-party dependency for validation (risk of major breaking changes)
  • Partial duplication with DataAnnotations for simple cases (Granit convention: use FluentValidation even for simple cases, for consistency)