ZeroZ Stack validation lives as annotations on your @DataModel POJOs — the single place your domain rules belong — and is enforced on both tiers from that one declaration:
- the Wasm client uses the rules for live form feedback (the binder),
- the server applies them automatically to every incoming RMI argument and every client-written shared signal value — so the rules hold even against a client that bypassed your UI entirely.
No reflection is involved: the annotation processor generates a <Model>_Rules class next to each constrained model at compile time, and the same generated code runs in the browser and on the JVM.
@DataModel
public class Registration {
@NotBlank @Size(min = 2, max = 40)
private String fullName;
@Min(0) @Max(50)
private int experience;
...
}Available constraints (in com.zeroz4j.api.validation):
| Annotation | Applies to | Meaning |
|---|---|---|
@NotBlank |
String | non-null and at least one non-whitespace character |
@Size(min, max) |
String | length within bounds (null not checked — combine with @NotBlank) |
@Min(value) |
numeric | ≥ value |
@Max(value) |
numeric | ≤ value |
Every annotation takes an optional message to override the generated default.
Attach the generated rule to a field — the field then validates on every change, carries the input-error style class once the user has touched it, and reports validity:
TextField nameField = new TextField("Full name");
nameField.bindValue(fullName); // two-way signal binding
nameField.withRule(Registration_Rules.fullName()); // annotation-driven validation
Computed<Boolean> formValid = new Computed<>(() -> ...); // combine per-field isValid()isValid() is accurate from the start (for form-level validity), while the error styling appears only after the first user interaction — untouched empty forms don't bleed red. getViolations() returns the messages for display next to the field.
<Model>_Rules.validate(obj) validates a whole object client-side (e.g. before enabling Submit).
Nothing to write. The RMI engine validates every incoming argument (including elements of List arguments) against the registered rules and rejects the call with a validation error before your service method runs. Client-written shared signal values pass through the same check. Client-side validation is UX; the server-side check is the one that decides, and it comes from the same annotations.
For rules that span fields or need server data ("username already taken"), validate inside your service method — annotations cover per-field constraints; business logic stays business logic.
- Constraints apply to String and numeric fields; nested objects are validated only when they arrive as RMI arguments themselves (no deep graph walking).
- The constraint set is deliberately small; it will grow as real usage demands (
@Patternis the likely next addition).