| name | event-sourcing-aggregate-validation |
| description | Use when defining event-sourced aggregate validation at command time, hook time, save time, or for computed snapshot/query mirror properties. |
| category | architecture |
| roles | ["architecture","coding","domain-driven-design"] |
| tags | ["event-sourcing","aggregate","validation","fluentvalidation","snapshots"] |
Event Sourcing Aggregate Validation Skill
Use this skill when defining validation strategy for event-sourced aggregates in Purview.EventSourcing.
Validation layers to apply
-
Command/event-raising validation
- Enforce transition and intent rules in generated hooks (
OnComputing...Event, OnRaising...Event, OnShouldApply...Event, On<Property>Changing).
- Reject invalid operations before events are recorded.
-
Aggregate model validation
- Use DataAnnotations on aggregate properties for structural constraints.
- Examples:
[Range], [Required], length constraints.
-
Save-time validation
- Save pipeline validates aggregates through
IAggregateValidator<TAggregate>.
- If no custom validator is provided,
DefaultAggregateValidator<TAggregate> runs DataAnnotations validation.
- If a FluentValidation
IValidator<TAggregate> is provided, it is adapted via FluentValidationAggregateValidator<TAggregate>.
-
Computed mirror validation
- For snapshot/query-facing mirror properties, derive values in
[Computed] hooks instead of trusting caller input.
- Keep canonical domain properties as the source for recomputing mirrored query shapes.
Rules
- Keep invariant checks close to mutation points (command/hook layer).
- Keep structural and shape constraints declarative (DataAnnotations/FluentValidation).
- Do not silently swallow validation failures.
- Treat
SaveResult<TAggregate>.IsValid and ValidationResult as first-class outcomes.
- Use
SaveResult.EnsureValid() when invalid saves must throw immediately.
- Preserve replay tolerance by avoiding command-time assumptions in hydration-only paths.
- For computed snapshot/query mirrors, validate derivation rules, not user-supplied values.
- If a save-time validator is added, prefer
IAggregateValidator<TAggregate> over ad-hoc validation inside stores.
Aggregate validation checklist
- Every command has explicit precondition checks.
- Property transitions are guarded (
On<Property>Changing / On<Property>Changed where needed).
- Aggregate-level attributes capture simple declarative constraints.
- Custom FluentValidation rules are used for cross-field/business policies not suited to attributes.
- Save handlers inspect or enforce
SaveResult.ValidationResult.
- Query-facing mirror properties are recomputed consistently from canonical state.
Output template to use
- Validation map by layer (command, aggregate model, save pipeline, computed mirrors).
- Hook methods and invariants per command (
OnComputing...Event, OnRaising...Event, OnShouldApply...Event, On<Property>Changing/Changed).
- Attribute-based constraints and rationale.
- FluentValidation rules (if any) and when they execute.
- SaveResult handling policy (
IsValid, EnsureValid, error propagation).
- Mirror-property derivation guarantees when query-facing fields exist.