Boundaries and Layout
SchemaBoundary and layout containers solve independent problems.
| Construct | Purpose | Changes database schema? | Creates UI structure? |
|---|---|---|---|
SchemaBoundary | Optionally tags collections with feature ownership | Adds featureTags to the orchestration result; collections compile normally | No |
FormStep | Defines a step around child render nodes | No | Yes |
ConditionalBlock | Defines conditional visibility | No | Yes |
MultiPopulator | Defines repeated entries and binds them to JSONB or a secondary collection | No; the referenced field or collection owns storage | Yes |
OrCondition | Defines alternative paths | No | Yes |
A boundary needs no group or step
const tagged = createFormSchemaOrchestrator([
SchemaBoundary(
{ featureId: 'crm', name: 'CRM' },
contacts,
projects,
),
])
This compiles direct render_collection nodes and records:
tagged.featureTags.contact === 'crm'
tagged.featureTags.project === 'crm'
The untagged form is equally valid:
const direct = createFormSchemaOrchestrator([contacts, projects])
Use a boundary only when feature ownership is useful.
Repeaters separate interaction from persistence
MultiPopulator always represents an array of repeated entries in the form
state. Its output says where that array will be persisted:
| Output strategy | Schema owner | Repeater entry shape |
|---|---|---|
separateTable | A child or explicitly referenced collection | The collection's fields |
jsonb | An existing RAW_JSON or ARRAY field with .strategy('jsonb') | The repeater's entryFields |
The repeater does not create storage invisibly. A secondary-table collection
still compiles as a normal collection. A JSONB field still belongs to its
parent collection and appears in databaseSchema.toJSONSchema().
This distinction keeps three concepts separate:
MultiPopulatorcontrols repeated form state.outputbinds that state to a persistence target.- The target field or collection defines the physical schema.
Add layout only for real interaction structure
const stepped = createFormSchemaOrchestrator([
FormStep('project', { showTitle: true }, projects),
FormStep('contacts', { showTitle: true }, contacts),
])
In the public 0.9.50 types, a SchemaBoundary accepts collections and nested
boundaries—not layout containers. Keep boundaries and layout as separate
top-level shapes.
Next: Schema Declaration Overview.