- 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>
61 lines
1.9 KiB
Markdown
61 lines
1.9 KiB
Markdown
---
|
|
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<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:
|
|
|
|
```csharp
|
|
// 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
|