22 — Advanced
Custom Validation Rules
Extend the ValidationRule base class to create your own domain-specific validators. Each custom rule defines its error code, severity, error message, and validation logic using @Given and @Otherwise annotations.
ValidationRule Base Class
The ValidationRule base class provides the following properties:
errorCode
Unique identifier for the validation error
severity
FATAL, ERROR, WARNING, or INFO
errorMessage
Template message with placeholders
defaultMessage
Fallback message when template fails
Example: Phone Number Validator
@Rule(name = "PhoneNumberValidationRule")
public class PhoneNumberValidationRule extends ValidationRule {
private final String fieldName;
public PhoneNumberValidationRule(String fieldName) {
super("phoneNumber.invalid", Severity.ERROR,
"${fieldName} must be a valid phone number.",
"Value must be a valid phone number.");
this.fieldName = fieldName;
}
@Given
public boolean isValid(@Param("fieldName") String value) {
if (value == null) return true; // Let NotNull handle nulls
return value.matches("^\\+?[1-9]\\d{1,14}$");
}
@Otherwise
public void otherwise(RuleViolations ruleViolations) {
ruleViolations.add(getRuleName(), getErrorCode(),
getSeverity(), getErrorMessage());
}
}
Using the Custom Rule
Rule rule = Rule.builder().build(new PhoneNumberValidationRule("phone"));
RuleViolations violations = new RuleViolations();
rule.run(phone -> "not-a-phone", ruleViolations -> violations);
violations.hasErrors(); // true
Lambda-Based Validation: SuppliedValidationRule
For simpler cases, use SuppliedValidationRule to create a validator from a lambda without writing a full class:
Condition check = condition((String email) ->
email != null && email.contains("@company.com"));
SuppliedValidationRule rule = new SuppliedValidationRule(
check, "corporate.email", Severity.ERROR,
"Must use a corporate email", "Must use a corporate email");
Severity Levels
Customize the severity of your validation rules to control how violations are treated:
| Severity | Description |
|---|---|
| Severity.FATAL | Critical error that should halt processing |
| Severity.ERROR | Validation failure that must be corrected |
| Severity.WARNING | Potential issue that should be reviewed |
| Severity.INFO | Informational notice, not a failure |
Customizing Error Codes & Messages
Error codes should follow a dot-separated naming convention (e.g., phoneNumber.invalid, email.corporate). Error messages support template placeholders like ${fieldName} that are resolved at runtime, while the default message serves as a fallback when template resolution fails.