Architecture
The entity system is a Vyuh plugin that turns a typed entity declaration into everything an enterprise screen needs: a typed CRUD API, reactive caches, declarative routes, layouts, editors, authorization gates, and a workspace shell. This page explains the package layering, the pieces a plugin owns, and the lifecycle that wires them together.
Package layers, one direction
| Package | Contents | Flutter dependency |
|---|---|---|
vyuh_entity_annotations | Pure-Dart annotations (@Entity, @Field, @ServerEntity), field companion declarations, UI layout declarations, relationship declarations, and the Authorize DSL. | None. |
vyuh_entity_generator | build_runner builders that emit client *.entity.dart files and server *.server.dart files from annotation metadata. | None at runtime; dev dependency only. |
vyuh_entity_system | EntityBase + mixins, EntityApi / HttpEntityApi, EntityConfiguration, EntityActions, layout descriptors (data-only), services container, drafts and versioning models, the AuthorizationProvider interface, plugin registration. | Minimal — only IconData. |
vyuh_entity_system_ui | Workspace shapes, route builder, list controller, default tab/version/audit layouts, editor widgets, error/import/export dialogs, formatting helpers. | Full Flutter widgets. |
The UI package depends on the core package; the core package never imports the UI package. Annotation metadata is shared through vyuh_entity_annotations. Regulated entity/action contracts live with the entity blueprint packages, while concrete IAM, policy, audit, and messaging adapters compose around the entity runtime.
Layered view at runtime
| Layer | Responsibility |
|---|---|
| Application | Declares features and entity configurations. |
| Plugin | Registers entities, generates routes, owns the services container, holds the AuthorizationProvider. |
| Core services | Caches reads, brokers cache invalidations, resolves names, polls realtime, drives search indexing. |
| UI | Hosts workspaces, drives selection ↔ URL ↔ panel state, renders layouts, runs editors. |
EntitySystemPlugin lifecycle
EntitySystemPlugin implements the standard Vyuh Plugin interface. It holds:
baseUrl— the API host allHttpEntityApis resolve against.authorizationProvider— synchronous resolver forAuthorizeexpressions. Defaults toOpenAuthorizationProvider(allow-everything) so a fresh app boots without authorization wiring.signatureProvider— optional, enablesSignatureDrivenEditor.filterWidgetDelegate— optional, supplies the filter UI.
Authorization wiring
The plugin treats authorization as a first-class concern. On every emission from vyuh.auth.userChanges it calls authorizationProvider.refresh(), which re-hydrates the actor's permissions, roles, and user-group memberships. The provider exposes synchronous reads (hasPermission, hasRole, isMemberOf, can(Authorize)) so layouts and actions can gate themselves without futures. See Permissions for the expression model.
Core services
| Service | Purpose |
|---|---|
EntityLocalizationService | Resolves locale-aware entity / layout / category names. |
EntityNameCacheService | In-memory cache of (entityType, id) → name for FK rendering. |
EntityHelpService | Per-entity contextual help bundles. |
SearchSyncService | Background sync of searchable entity records. |
EntityRealtimeService | Polling-based change detection. |
QueryCacheService | Stale-while-revalidate cache + mutation announcement bus. |
SignatureVerificationService | E-signature verification (only when signatureProvider is set). |
Services are registered through EntityServicesContainer keyed by their runtime Type. Anyone can fetch them via vyuh.entity?.services.get<T>() or tryGet<T>().
Registering an entity
Entities are registered via an EntityExtensionDescriptor inside a feature:
final lmsFeature = FeatureDescriptor(
name: 'lms',
title: 'Learning Management System',
extensions: [
EntityExtensionDescriptor(
entities: [courseConfig, trainerConfig, certificationConfig],
services: [EntityServiceRegistration(MyDomainService())],
fieldFormatters: [
FieldFormat(field: 'instructor_id', formatter: trainerFormatter),
],
hierarchies: [areaHierarchy],
),
],
);The descriptor surfaces four kinds of contribution:
- Entities — each
EntityConfiguration<T>is registered with the plugin. - Services — domain services that need lifecycle management.
- Field formatters — declarative
FieldFormatrules that apply across the app, matched by literal field name or regex pattern. - Hierarchies — composite tree definitions used by hierarchy widgets.
Once registered, calling plugin.generateRoutes() walks every entity whose metadata.visibility includes a RouteVisibility (or MenuVisibility) and delegates route construction to its routing.builder.
From config to URLs
StandardRouteBuilder<T> (in the UI package) turns one EntityConfiguration<T> into a tree of GoRoutes organised by slot: createRoute, editRoute, dashboardRoute, viewTabsRoute, customRoutesSlot, singletonMainRoute, singletonEditRoute, singletonTabsRoute. Subclasses override buildSingleRoute / buildMultipleRoutes and dispatch on the slot name to substitute one route without rebuilding the whole tree.
URLs are never hand-constructed by callers. EntityConfiguration.route (alias for routing.path) returns a NavigationPathBuilder with helpers for list, view, edit, viewTab, create, dashboard, and custom. Two factory constructors cover the two patterns:
| Factory | Use case | URL shape |
|---|---|---|
NavigationPathBuilder.collection(prefix:) | Multi-instance entities | /prefix, /prefix/:id, /prefix/:id/edit |
NavigationPathBuilder.singleton(prefix:) | Single-instance entities | /prefix, /prefix/edit (id ignored) |
See the API reference for the full method surface.
Workspace shapes
Routes are mounted inside a workspace. Workspaces are StatefulWidgets that own URL ↔ selection ↔ panel synchronisation, controller lifecycles, and mutation event dispatch. They compose by mixin so a new shape only adds the capabilities it needs.
| Workspace | Role |
|---|---|
EntityWorkspace<T> | Standard list-with-detail. Owns an EntityListController<T> and a cdx_panes workspace. |
SingletonEntityWorkspace<T> | Singleton entity host — no list, no row selection, just the active route's content. |
GroupedEntityWorkspace | Tabbed shell hosting child workspaces (used by Inbox-style screens). |
All three extend the shared WorkspaceTemplate base, which standardises:
- A single
GoRouterlistener for URL change reactions. - A central disposers list so capability mixins can register teardown.
- A
QueryCacheService.invalidationssubscription that fans out typedMutationEvents to the workspace'sonAfterMutationcallback.
See Layouts for how layout slots compose with each shape.
Where each subsystem lives
| Concern | Package | Key types |
|---|---|---|
| Annotation metadata | annotations | Entity, Field, ServerEntity, FieldCompanion, Authorize |
| Code generation | generator | entityBuilder, serverEntityBuilder |
| Regulated runtime contracts | runtime_core | AccessExpression, PolicyResolver, OperationEnvelope, EvidenceEnvelope, OutboxMessage |
| Identity & audit | core | EntityBase, Auditable, Versionable, Draftable |
| CRUD | core | EntityApi, HttpEntityApi, EndpointBuilder |
| Caching | core | QueryCacheService, Mutation, CachingPolicy |
| Authorization | core | AuthorizationProvider, Authorize (annotations) |
| Configuration | core | EntityConfiguration, EntityRouting, EntityActions, EntityLayouts |
| Forms | core | EntityEditor, StandardEntityEditor, SignatureDrivenEditor, editor parts |
| Drafts & versioning | core | DraftMetadata, EntityVersion, EntityAudit |
| Workspaces | UI | EntityWorkspace, SingletonEntityWorkspace, GroupedEntityWorkspace |
| Routes | UI | StandardRouteBuilder, EntityCustomRoute, NavigationRouteBuilder |
| Reactive lists | UI | EntityListController, EntityListView, EntityView |
| Default layouts | UI | version / audit / item-table / delta-table layouts |
Next steps
- Entity Model —
EntityBase, mixins, references, schema-type identifier. - Configuration —
EntityConfiguration<T>slot by slot. - Permissions —
AuthorizeDSL and theAuthorizationProvider. - Data Flow — read paths, cache invalidation, drafts, list controller.
- Defining Entities — hands-on guide.