ADR 0009: Rules target logical places and semantic data types
Status: accepted
Status: accepted
Context
In 1.0.0-alpha.1 a rule targeted an entity, optionally a component and fields. Two things were
missing. A user interface with many screens evaluated every rule of the entity to show the findings
of one screen. And a rule about a kind of value, such as the format of an e-mail address, had to be
copied for every field that held one.
Decision
A target may name a place: page, screen, section and component, each a lower-case
kebab-case id that the ruleset and the user interface agree on. A request may carry a view with
the same four members. A rule is skipped when the view and the target both name a level and the
names differ; a rule that names no place applies in every view, and a request without a view
evaluates everything. A finding reports the place of its rule as location.
A target may instead name a semantic type declared under types. Fields are bound to types in
entities.<Name>.fieldTypes, and a rule that targets a type runs once per bound field, with value
and field in scope. Only validation rules may target a type. A child ruleset may bind more fields
and may not rebind one (FIELD_TYPE_REBOUND).
Who owns a rule stays in scope; what a rule is about is the target.
Consequences
- A screen evaluates its own rules, and a finding can be shown where it belongs without a mapping table in the user interface.
- Place ids are an interface between the ruleset and every user interface. Renaming one is a
breaking change.
finding.componentbecamefinding.location. - The server normally sends no view: the decision covers every place.
- One rule covers every field of a type, including fields bound later by a child.
- A binding is one pointer, so a type rule does not iterate over the elements of a list; that
remains
forEach.
Alternatives considered
- CSS selectors, XPath or DOM ids as targets. They tie a rule to one technology and one version of its markup, break on a redesign, mean nothing to a mobile application or an API, and cannot be checked when the ruleset is loaded.
- A ruleset per page, with pages as scope levels. The scope is a single chain of owners; pages are not owners, and one entity appears on many pages.
- Types inferred from JSON Schema
formator from field names. Implicit and fragile; a renamed field would silently lose its rules. An explicit binding is checked at load. - Copying rules per field, as before. The copies drift.