Skip to content

Payload validation

Reject malformed input before application code runs. Add Jakarta constraints to the command or query fields that need them; Fluxzero checks the payload before invoking its handler.

If a constraint is violated, the handler method is never called. Instead, a ValidationException is thrown before the handler is invoked.

public record CreateUser(@NotBlank String name) {
}

A blank name is rejected. Kotlin’s @field: makes the constraint apply to the backing field.

Use @Valid to validate the contents of a value object, and @NotNull when the value itself is required:

public record UserProfile(@NotBlank String name) {}
public record CreateUser(@NotNull @Valid UserProfile profile) {}

@Valid alone does not reject null. Kotlin’s non-null constructor value also constrains deserialization.

The SDK’s built-in Jakarta Validation implementation supports standard constraints, nested collections, groups, custom validators and method constraints. Hibernate Validator is not required for normal SDK payload validation.

The default Fluxzero payload validator checks field constraints, including container-element constraints and field-based @Valid cascades, before the containing object’s method constraints. Pure constraint methods may dereference values required by active field constraints without repeating null guards. For example:

public record ConfigureReminder(@NotNull Duration delay) {
@AssertTrue(message = "Choose a non-negative delay.")
boolean hasNonNegativeDelay() {
return !delay.isNegative();
}
}

For collections, require both the container and its elements, for example @NotNull List<@NotNull @Valid ReminderDetails> reminders. @Valid checks a present nested value; it does not make a missing value or element invalid. Use @NotNull or the appropriate field constraint separately. Optional values still need a null-aware rule. Conditionally required values need a combination rule.

This applies to automatic payload validation and assertValid/checkValidity/isValid with the default validator. It is not a promise that a method is never called for invalid input: after finding field failures, the validator may try method constraints again to collect additional violations, suppressing failures from that diagnostic pass. Keep constraint methods pure. The raw getConstraintViolations/Jakarta validate APIs do not suppress such method exceptions; use the normal payload-validation API for this contract.

Only constraints in the active groups and reached through enabled cascades establish these preconditions. A requirement in a later group-sequence stage cannot protect a method in an earlier stage. Method-level @Valid return values are not prevalidated fields. A replacement validator owns its own ordering and failure behavior.

Constraint methods on a payload may request contextual parameters that the SDK’s default validator can inject while a message is being handled. It uses the same resolver set as handler method injection, so values such as User, Message, DeserializingMessage, Metadata, and custom resolver values can be used directly.

public record CreateUser(@NotBlank String userId) {
@AssertTrue(message = "Only admins may create admin users")
boolean allowedBy(User user, Message message) {
return !userId.startsWith("admin-") || user != null && user.hasRole("admin");
}
}

You can disable this validation entirely by calling: Assuming you are configuring a FluxzeroBuilder builder:

builder.disablePayloadValidation();

Of course, it’s also easy to provide your own validation if desired. Use replaceValidator(...) on the FluxzeroBuilder to replace the configured validator. Convenience methods on ValidationUtils delegate to the validator of the current Fluxzero instance when one is bound, and otherwise fall back to ValidationUtils.defaultValidator.


© 2026 Fluxzero