Skip to content

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 — the Set<UIVisibility> that controls menu / search / dashboard / route exposure. Empty set = invisible everywhere; routes are generated only when RouteVisibility (or MenuVisibility) is present.
  • isSingleton — flips the registration into singleton mode (no list, no selection).
  • priority and category — 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 — an EndpointBuilder that constructs URLs.
  • fromJson — entity factory.
  • defaultSortField and hasDrafts — 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:

FieldRole
pathNavigationPathBuilder for URL construction.
builderNavigationRouteBuilder<T> that turns the config into GoRoutes — typically StandardRouteBuilder<T>.
modeEntitySelectionMode (navigate, none, responsive).
permissionsOptional EntityRoutePermissions with permission strings per route.
FactoryUse caseBehaviour
NavigationPathBuilder.collection(prefix:)Multi-instance entities:id appears in view/edit URLs.
NavigationPathBuilder.singleton(prefix:)Single-instance entitiesid 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

ModeBehaviour
navigateDefault. Tap a row to push the detail route.
noneNo navigation; only fires onSelectionChanged. Used by pickers.
responsiveNavigate 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:

SlotTypePurpose
listList<CollectionLayout<T>>Required. Table / grid / kanban renderings.
detailsList<EntityLayout<T>>Detail-page tabs.
summaryEntityLayout<T>?Compact card used in references and singleton main views.
dashboardAggregateLayout<T>?Aggregate landing page.
analyticsList<AggregateLayout<T>>Charts and trend visualisations.
itemsList<EntityItemLayout<T>>Single-entity primitives for snapshots (version detail, palette previews). Framework provides a default field/value table.
comparisonsList<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:

SlotTypeWhere it appears
inlineList<EntityAction<T>>Detail view header / toolbar. Required.
menuList<ActionGroup<T>>Grouped actions in dropdown menus.
collectionList<CollectionAction<T>>Bulk operations on selected rows.
headerList<CollectionAction<T>>Prominent buttons above the list (e.g. Create). Use StandardEntityActions.header<T>().

Each EntityAction<T> carries:

  • An icon, title (or titleResolver), and async handler.
  • Optional isVisible and isEnabled predicates for state-based gating.
  • An optional disabledTooltip resolver.
  • An optional typed Authorize expression 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:

EditorWhen 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, save runs 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 — declarative FieldDefinition<T> list. Layouts, forms, and filters reference these, giving compile-time safety. See Entity Model.
  • filterPresets — system-defined FilterPresets shown as quick-action buttons in the filter dialog. Built with FilterPresetHelper.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 through FieldFormat entries.

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) and EntityActions.function(factory) — eager-evaluated factories that defer construction until config wiring, avoiding ordering issues at app startup.
  • EntityLayouts.filterByPermission(authorizeResolver) — strips layouts whose Authorize expression denies for the current actor; used by the workspace shell at mount time.

Registering a configuration

Configurations are registered through an EntityExtensionDescriptor:

dart
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:

dart
final route = vyuh.entity?.getConfig<Course>()?.route;
context.go(route!.view(courseId));

Next steps