Implementing Data Validation
Layer Validates
Edge (controller) Format, presence, type
Service Cross-field, multi-aggregate
Domain Invariants in entity
DB Constraints (defense-in-depth)
Example: Trigger validation in controller
@ PostMapping ( "/orders" )
public OrderResponse create (@ Valid @ RequestBody CreateOrderRequest req) { ... }
2. Implementing Business Rule Validation
Example: Enforce in domain
public void approve (Approver a) {
if (status != PENDING) throw new BusinessRuleException ( "only pending can be approved" );
if (a. equals (submittedBy)) throw new BusinessRuleException ( "submitter cannot approve" );
this .status = APPROVED;
}
3. Using Validation Annotations
Annotation Use
@NotNull/@NotBlankPresence
@SizeLength
@Min/@Max/@DecimalMinRange
@PatternRegex
@EmailRFC email
@ValidCascade
@AssertTrue/@AssertFalseBoolean
@Past/@Future/@PastOrPresentTime
4. Implementing Custom Validators
Example: Custom annotation
@ Constraint ( validatedBy = StrongPasswordValidator.class)
@ Target ({FIELD}) @ Retention (RUNTIME)
public @ interface StrongPassword {
String message () default "weak password" ;
Class <?> [] groups () default {};
Class <? extends Payload > [] payload () default {};
}
public class StrongPasswordValidator implements ConstraintValidator < StrongPassword , String > {
public boolean isValid (String v , ConstraintValidatorContext c ) {
return v != null && v. length () >= 12 && v. matches ( ".* \\ d.*" ) && v. matches ( ".*[A-Z].*" );
}
}
5. Implementing Validation Error Messages
Practice Detail
i18n key {order.lines.required}
Resource bundle ValidationMessages_en.properties
Field path Include in response
Code + message Machine-readable + human
6. Implementing Cross-Field Validation
Example: Class-level constraint
@ DateRangeValid // class-level
public record DateRange (LocalDate from, LocalDate to) {}
public class DateRangeValidator implements ConstraintValidator < DateRangeValid , DateRange > {
public boolean isValid (DateRange v , ConstraintValidatorContext c ) {
return v. from () == null || v. to () == null || ! v. from (). isAfter (v. to ());
}
}
7. Implementing Validation Groups
Use Detail
Different rules per op Create vs Update
Marker interface interface OnCreate {}
Apply @Validated(OnCreate.class)
Threat Sanitizer
XSS OWASP Java HTML Sanitizer
SQL injection Parameterized queries (don't sanitize, use binds)
Path traversal Canonicalize + allow-list base dir
CSV injection Prefix dangerous chars
9. Implementing Schema Validation
Format Tool
JSON JSON Schema (networknt/json-schema-validator)
XML XSD
Avro/Protobuf Schema registry (Confluent, Buf)
OpenAPI Request/response validation middleware
10. Understanding Fail-Fast vs Collect-All Validation
Mode When
Fail-fast Internal calls; quick error
Collect-all UI forms; show all errors at once
Bean Validation default Collect-all per object
11. Implementing Fluent Validation
Example: Fluent rules
Validator< User > v = Validator. < User > builder ()
. require (u -> u. name () != null , "name.required" )
. check (u -> u. age () >= 18 , "age.adult" )
. check (u -> u. email (). contains ( "@" ), "email.invalid" )
. build ();
ValidationResult r = v. validate (user);
12. Implementing Domain Model Validation
Where What
Constructor Reject invalid construction
Mutator Reject invalid transitions
Aggregate root Multi-entity invariants
Note: Always-valid domain objects mean callers cannot construct an invalid state.