Replace custom ValidationBuilder with FluentValidation#112
Merged
antosubash merged 1 commit intomainfrom Apr 15, 2026
Merged
Conversation
Delete framework/SimpleModule.Core/Validation/ValidationBuilder.cs (140 LOC) and ValidationResult.cs plus their tests (160 LOC). The custom fluent builder reimplemented a narrow subset of what FluentValidation has provided for over a decade. Add FluentValidation 12.1.1 and FluentValidation.DependencyInjectionExtensions via central package management. Reference both from SimpleModule.Core so IValidator<T> and AddValidatorsFromAssembly flow transitively into modules without per-module csproj edits. Convert 17 static Validate(request) methods into AbstractValidator<T> classes across Tenants, Products, Orders, PageBuilder, RateLimiting, Map, and Email. Inject IValidator<TRequest> into the 20 endpoint lambdas that use them; switch to async lambdas with ValidateAsync. Preserve the existing RFC 7807 response shape — GlobalExceptionHandler already emits ProblemDetails with an `errors` extension matching FluentValidation's ToDictionary(). Keep SimpleModule.Core.Exceptions. ValidationException as the general-purpose "validation failed" exception thrown elsewhere (Chat, UserService, EmailService). Bridge the two with a new ValidationResultExtensions.ToValidationErrors() helper. Register validators per-module via services.AddValidatorsFromAssembly Containing<ThisModule>() in ConfigureServices. Two new PageBuilder validator files extracted from previously-inline validation in CreateEndpoint/UpdateEndpoint. Update the sm CLI's FallbackValidator template and two skill docs (simplemodule/endpoints.md, minimal-api/patterns.md) to reflect the new pattern. Historical references in docs/plans/* and docs/specs/* are left unchanged as records of past decisions. All 1195 tests pass (12 deleted framework-validation tests accounted for).
Deploying simplemodule-website with
|
| Latest commit: |
d9402e8
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://bac6da1f.simplemodule-website.pages.dev |
| Branch Preview URL: | https://feature-fluent-validation-mi.simplemodule-website.pages.dev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replace the custom
framework/SimpleModule.Core/Validation/ValidationBuilder.cs(140 LOC) +ValidationResult.cswith FluentValidation 12.1.1. The custom fluent builder reimplemented a narrow subset of what FluentValidation has provided for over a decade.Validate(request)methods →AbstractValidator<T>classesIValidator<TRequest>and callValidateAsyncservices.AddValidatorsFromAssemblyContaining<ThisModule>()GlobalExceptionHandleralready emitted RFC 7807ProblemDetailswith anerrorsextension, which matches FluentValidation'sToDictionary()outputMigration map
public static class XxxValidatorwithValidate(req)public sealed class XxxValidator : AbstractValidator<T>new ValidationBuilder().AddErrorIf(...).Build()RuleFor(x => x.Prop).NotEmpty()...ValidationResult.IsValid/.ErrorsFluentValidation.Results.ValidationResult.IsValid/.Errorsthrow new ValidationException(validation.Errors)throw new Core.Exceptions.ValidationException(validation.ToValidationErrors())Added
ValidationResultExtensions.ToValidationErrors()as a shared helper to bridge FluentValidation'sValidationResult→ theDictionary<string, string[]>shape thatValidationExceptionand the RFC 7807 writer consume.Kept the existing
ValidationExceptionSimpleModule.Core.Exceptions.ValidationExceptionis the general-purpose "validation failed" exception thrown from many places outside endpoint validators (Chat, UserService, EmailService, PageBuilderService). Removing it would have broadened the PR beyond validation-layer concerns. FluentValidation's ownValidationExceptionis disambiguated by fully qualifying asCore.Exceptions.ValidationExceptionin the 20 migrated endpoints.Notable edge cases
UpdateDefaultMapRequestValidatortook a runtime parameter (int maxLayersfromIOptions<MapModuleOptions>). FluentValidation handles this via constructor injection — the registered validator receivesIOptions<MapModuleOptions>and readsmaxLayersonce at construction.RuleForEachonItemswas rewritten as aCustomrule to preserve the aggregated"Items"error-field shape (FluentValidation'sRuleForEachwould produce keys like"Items[0].Quantity").CreateEndpoint.cs/UpdateEndpoint.cs; extracted intoCreatePageRequestValidator.cs/UpdatePageRequestValidator.cs.SendEmailRequestValidatorwas removed — FluentValidation's built-in.EmailAddress()rule replaces it across all three email validators.Documentation updated
.claude/skills/simplemodule/references/endpoints.md— validation section rewritten.claude/skills/minimal-api/references/patterns.md— validation section rewrittencli/SimpleModule.Cli/Templates/FeatureTemplates.cs—FallbackValidatortemplate and in-line body replacement now emitAbstractValidator<T>Historical references in
docs/plans/*anddocs/specs/*were left unchanged as records of past decisions.Test plan
dotnet build— 0 errors, 21 node-modules warnings (unrelated, pre-existing)dotnet test— 1195 passed, 0 failed, 1 skipped (pre-existing). 12 fewer than main becauseValidationBuilderTests(10) andValidationResultTests(2) were deleted along with the types they tested.ValidationBuilder,SimpleModule.Core.Validation.ValidationResult, or the old static*Validator.Validate(request)call pattern anywhere in the codebasePart of a series
PR 2 of 3 replacing custom framework code with trusted OSS libraries:
Custom cache → FusionCache(merged: Replace custom ICacheStore with FusionCache #111)ValidationBuilder→ FluentValidationIEventBus→ Wolverine (pending)Based on latest
main(includes #111).