Rule Cascade
ReferenceDecision records (ADRs)

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.component became finding.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 format or 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.

On this page