Skip to main content
An order has a shipping address, and the address has rules of its own. valid() checks them, and every violation they report is nested under the property, so the path says exactly where it is: shipping.zipCode, not zipCode.
ShippingAddress.kt
Order.kt
Nesting goes as deep as the data does: an address inside a store inside a company reports company.store.address.street.

Which rules valid() checks

valid has a form for each way of declaring rules:

Optional and repeated objects

valid() goes inside the block of required or ifPresent, like any constraint, so null is decided first. An optional billing address is checked only when it is there:
For a list of objects, each runs valid() on every element, and the index joins the path: items[1].quantity.

The order of the violations

Violations come in the order the rules are declared, with one refinement: inside one property, its own constraints come before the violations of its nested objects and elements. In ::items required { each { valid() }; notEmpty() }, a notEmpty violation is reported before the violations of the items, whatever the order of the two lines.
valid() counts as one rule. Inside failFast it is one step, with all the violations of the nested object.