Declarative UI DSL with embedded permissions, validation, and actions
Declarative UI DSL with embedded permissions, validation, and actions
Operational screens (application detail, repayment lists, collections, decision views) are YAML DSL documents — never hand-built React per screen. Permissions, validation constraints, conditional visibility, KPIs, tables, row actions, and decision blocks are all data, compiled to runtime by the renderer.
Problem it solves
- Hand-built screens per customer/role don't scale and drift from backend permissions; bespoke JS per form is unreviewable. The DSL makes UI a reviewable, versioned artifact (git diff = UI change) with permissions enforced in the definition, not sprinkled through components.
Shape
- UIForm DSL v2
mode: applications: screen = header (badges, facts, actions) + KPIs + tabs of blocks (section/detail/table/upload/decision). - Permissions as data:
read/writerole lists on screen, block, field, and action — enforced by the renderer, previewable per role. - Conditional logic as data:
visible_if/required_if/disabled_ifwithall/any/nested conditions over record fields. - Tables: keyset pagination (
cursor_param/limit_param), filters as params, row actions withconfirm+success.toast+refreshlists — the UI declares its own invalidation. - Data binding:
data_sourceswithresponse.record_path, cache TTL, prefill maps;{{record.*}}templating everywhere. - Decision blocks render fail-safe outcomes (DECLINE/REFER/APPROVE) with per-rule detail — UI and decision engine share the same facts.
Rules
- Permissions live in the DSL and mirror backend enforcement — the DSL is the first gate, never the only one.
- Every mutation declares confirm text, success feedback, and what to refresh.
- Keyset pagination for large tables — never offset pagination on growing datasets.
- Validation constraints carry custom messages; async validation via
async_validateendpoint for server-side checks. - Screens are YAML in git; changes are PRs through the compile/validation gates (see
kb:yaml-dsl-form-runtime).
Anti-patterns
- Role checks only in React components (bypassable, untested).
- Offset pagination on transaction tables.
- Actions without confirm/refresh — stale UI after mutation.
- DSL escaping into bespoke per-screen JavaScript (drift, unreviewable).
- Decision UI showing outcomes without the rule detail that produced them (audit gap).
Sources
- app-builder/DESIGN.md (§3.9 Applications Mode, permissions, conditions, hooks, tables)
- factory/forms/form-engine, factory/forms/form-edit-custom
- factory/ui-security/ (probes, perf-contract, management report)