Configuration
EntityConfiguration<T> is the single descriptor that ties together every runtime concern for an entity type — what it looks like, where it lives in the URL, how it loads data, how it renders, how it's edited, and which actions it exposes. Anything the framework needs to know about a Course, a LogTemplate, or a Settings singleton lives on its EntityConfiguration<T> instance.
This page covers the slots; for individual API signatures see the EntityConfiguration API.
Anatomy
metadata, api, routing, layouts, and actions are required; the rest are optional and let you incrementally enrich the entity.
metadata — identity and visibility
EntityMetadata declares what the entity is and where it appears. Covered on the Entity Model page. The configuration-time choices that matter most:
identifier— used for registration, permission lookups, and palette search.visibility— theSet<UIVisibility>that controls menu / search / dashboard / route exposure. Empty set = invisible everywhere; routes are generated only whenRouteVisibility(orMenuVisibility) is present.isSingleton— flips the registration into singleton mode (no list, no selection).priorityandcategory— drive sort order in menus.
api — the CRUD contract
EntityApi<T> is the abstract CRUD interface. The standard implementation, HttpEntityApi<T>, talks REST through vyuh.network and authenticates via vyuh.auth. The required ingredients are:
entityType— short type name used in cache keys and mutation announcements (e.g.'courses').schemaType— schema-qualified type name (e.g.'lms.course').pathBuilder— anEndpointBuilderthat constructs URLs.fromJson— entity factory.defaultSortFieldandhasDrafts— list-time defaults.
HttpEntityApi includes built-in stale-while-revalidate caching with mutation-driven invalidation. See Data Flow for the read/write paths.
routing — URLs and access
EntityRouting<T> consolidates four concerns:
| Field | Role |
|---|---|
path | NavigationPathBuilder for URL construction. |
builder | NavigationRouteBuilder<T> that turns the config into GoRoutes — typically StandardRouteBuilder<T>. |
mode | EntitySelectionMode (navigate, none, responsive). |
permissions | Optional EntityRoutePermissions with permission strings per route. |
NavigationPathBuilder
| Factory | Use case | Behaviour |
|---|---|---|
NavigationPathBuilder.collection(prefix:) | Multi-instance entities | :id appears in view/edit URLs. |
NavigationPathBuilder.singleton(prefix:) | Single-instance entities | id ignored; create aliases to edit. |
Both expose a uniform method surface (view, edit, viewTab, create, list, dashboard, custom) so call sites can navigate without caring which factory built the entity.
EntitySelectionMode
| Mode | Behaviour |
|---|---|
navigate | Default. Tap a row to push the detail route. |
none | No navigation; only fires onSelectionChanged. Used by pickers. |
responsive | Navigate on desktop, embed inline on mobile. |
Route permissions vs. action authorization
EntityRoutePermissions is the only place the framework still uses bare permission strings (List<String>?) — for the high-level guard that decides "can this user reach /lms/courses/new at all?". Everything else (layouts, actions, header actions, field-level visibility) uses the typed Authorize DSL described on the Permissions page.
layouts — visual surfaces
EntityLayouts<T> groups every renderable surface for the entity. See Layouts for the slot-by-slot detail. The fields:
| Slot | Type | Purpose |
|---|---|---|
list | List<CollectionLayout<T>> | Required. Table / grid / kanban renderings. |
details | List<EntityLayout<T>> | Detail-page tabs. |
summary | EntityLayout<T>? | Compact card used in references and singleton main views. |
dashboard | AggregateLayout<T>? | Aggregate landing page. |
analytics | List<AggregateLayout<T>> | Charts and trend visualisations. |
items | List<EntityItemLayout<T>> | Single-entity primitives for snapshots (version detail, palette previews). Framework provides a default field/value table. |
comparisons | List<EntityComparisonLayout<T>> | Multi-entity primitives for diffs (audit deltas). Framework provides a default field/value delta table. |
Every layout accepts an optional Authorize expression. EntityLayouts.filterByPermission returns a copy with hidden layouts removed.
actions — interactive operations
EntityActions<T> collects every interactive operation that targets one or many entities:
| Slot | Type | Where it appears |
|---|---|---|
inline | List<EntityAction<T>> | Detail view header / toolbar. Required. |
menu | List<ActionGroup<T>> | Grouped actions in dropdown menus. |
collection | List<CollectionAction<T>> | Bulk operations on selected rows. |
header | List<CollectionAction<T>> | Prominent buttons above the list (e.g. Create). Use StandardEntityActions.header<T>(). |
Each EntityAction<T> carries:
- An icon, title (or
titleResolver), and asynchandler. - Optional
isVisibleandisEnabledpredicates for state-based gating. - An optional
disabledTooltipresolver. - An optional typed
Authorizeexpression for authorization gating.
Authorization and visibility compose with AND semantics — both must pass for the action to render.
editor — create and edit forms
EntityEditor<T> is the abstract base for any editor. Two concrete implementations cover the common cases:
| Editor | When to use |
|---|---|
StandardEntityEditor<T> | Non-regulated entities. Constructor params for parts, transformer, display mode, drafts. Default actions: save + cancel, no verification. |
SignatureDrivenEditor<T> | Regulated (pharma) entities. Resolves a LifecycleRequirements value via a LifecycleResolver callback during init, then bakes verification + remarks requirements into the save action. |
Editors compose parts — units like FormEditorPart<T> and CustomEditorPart<T> — that render as tabs when there is more than one. A single-part editor renders the part directly without a tab strip. The EntityTransformer<T> declares how form data maps to and from the entity (DefaultEntityTransformer<T>(fromJson: ...) covers the @JsonSerializable case).
Display mode is one of EditorDisplayMode.panel (side panel), fullWidth, or adaptive.
SignatureDrivenEditor decides at init:
- If approvals are configured for the operation, all saves go to draft and verification happens later through the workflow system.
- Otherwise,
saveruns inside the verification dialog so remarks flow naturally back into the API call.
See Forms and Editors for hands-on patterns.
fields, filterPresets, referenceFields
fields— declarativeFieldDefinition<T>list. Layouts, forms, and filters reference these, giving compile-time safety. See Entity Model.filterPresets— system-definedFilterPresets shown as quick-action buttons in the filter dialog. Built withFilterPresetHelper.createPresets.referenceFields— generator-populated map of FK column → target entity identifier. Server emitters consume this for cross-entity FK metadata; UI-side FK rendering is wired by features throughFieldFormatentries.
Composition helpers
EntityConfiguration.copyWith(...)— feature packages can extend a base config from a shared entity package (e.g. add a workflow tab without rewriting the entire definition).EntityRouting.function(factory)andEntityActions.function(factory)— eager-evaluated factories that defer construction until config wiring, avoiding ordering issues at app startup.EntityLayouts.filterByPermission(authorizeResolver)— strips layouts whoseAuthorizeexpression denies for the current actor; used by the workspace shell at mount time.
Registering a configuration
Configurations are registered through an EntityExtensionDescriptor:
EntityExtensionDescriptor(
entities: [courseConfig, trainerConfig, certificationConfig],
)Direct registration is also available for tests and bootstrap code: plugin.register<Course>(courseConfig). Retrieve a config with vyuh.entity?.getConfig<T>() (preserves the type) or getConfigByIdentifier(...) (returns EntityConfiguration<EntityBase> — type-erased; only use it when the typed variant is impossible).
For navigation, prefer the configuration's route getter (alias for routing.path) — never hardcode URLs:
final route = vyuh.entity?.getConfig<Course>()?.route;
context.go(route!.view(courseId));Next steps
- Layouts — every layout slot in detail.
- Permissions —
Authorizeexpressions and the provider model. - Data Flow — how the API, cache, and list controller cooperate.
- Defining Entities — concrete walkthrough.