Services
The entity system uses a service-oriented architecture with a central plugin that manages registration, route generation, authorization, and a container of pluggable services.
EntitySystemPlugin
class EntitySystemPlugin extends Plugin with RouteObservers — the main plugin that ties the entity system together.
Constructor
EntitySystemPlugin({
required this.baseUrl,
AuthorizationProvider? authorizationProvider,
this.signatureProvider,
this.filterWidgetDelegate,
});| Parameter | Type | Default | Description |
|---|---|---|---|
baseUrl | String | -- | Base URL for all API endpoints |
authorizationProvider | AuthorizationProvider? | OpenAuthorizationProvider() | Authorization implementation (synchronous after refresh) |
signatureProvider | SignatureVerificationProvider? | null | E-signature verification provider |
filterWidgetDelegate | FilterWidgetDelegate? | null | Optional delegate for filter UI (typically supplied by cdx_feature_query) |
Properties
| Property | Type | Description |
|---|---|---|
baseUrl | String | API base URL |
authorizationProvider | AuthorizationProvider | Active authorization provider |
signatureProvider | SignatureVerificationProvider? | Signature verification provider |
filterWidgetDelegate | FilterWidgetDelegate? | Filter UI delegate |
services | EntityServicesContainer | Type-safe service container |
routeObserver | RouteObserver<PageRoute> | Navigation observer |
registeredTypes | List<Type> | All registered entity Dart types |
registeredIdentifiers | List<String> | All registered entity identifiers (incl. alternates) |
registeredConfigs | List<EntityConfiguration<EntityBase>> | All configs sorted by menu category, priority, name |
configsWithMetadata | Map<EntityConfiguration<EntityBase>, EntityMetadata> | Configs mapped to metadata |
Registration
| Method | Signature | Description |
|---|---|---|
register<T> | void register<T extends EntityBase>(EntityConfiguration<T> config) | Register entity (throws if already registered) |
unregister<T> | bool unregister<T extends EntityBase>() | Unregister an entity type |
unregisterByIdentifier | bool unregisterByIdentifier(String identifier) | Unregister by identifier |
clear | void clear() | Unregister all entities |
Retrieval
| Method | Signature | Description |
|---|---|---|
getConfig<T> | EntityConfiguration<T>? getConfig<T extends EntityBase>() | By Dart type (preserves generic) |
getConfigByIdentifier | EntityConfiguration<EntityBase>? getConfigByIdentifier(String id) | By identifier (type erased) |
isRegistered<T> | bool isRegistered<T extends EntityBase>() | Whether type is registered |
isIdentifierRegistered | bool isIdentifierRegistered(String identifier) | Whether identifier is registered |
getMetadata<T> | EntityMetadata? getMetadata<T extends EntityBase>() | Metadata for a type |
getRegistrationSummary | Map<String, String> getRegistrationSummary() | Identifier → type-name mapping |
TIP
Prefer getConfig<T>() over getConfigByIdentifier() to preserve the entity's generic type. The identifier-based method returns EntityConfiguration<EntityBase>, which causes type erasure and breaks typed callbacks downstream.
Route generation
List<RouteBase> generateRoutes();Generates GoRouter routes for every registered entity whose metadata has hasRoutes == true (i.e. visibility contains RouteVisibility or MenuVisibility).
Service registration
Future<void> registerService<T extends EntityService>(T service);Registers a service in the container and immediately initializes it. The generic type T is preserved for type-safe retrieval via services.get<T>().
Platform extension
extension EntitySystemExtension on VyuhPlatform {
EntitySystemPlugin? get entity => getPlugin<EntitySystemPlugin>();
}Usage: vyuh.entity?.services.queryCache, vyuh.entity?.authorizationProvider.
Core services registered automatically
During init() the plugin registers and initializes:
EntityLocalizationServiceEntityNameCacheServiceEntityHelpServiceSearchSyncServiceEntityRealtimeService(withPollingRealtimeStrategyby default)QueryCacheServiceSignatureVerificationService(only whensignatureProvideris supplied)
It also binds vyuh.auth.userChanges to authorizationProvider.bindRefreshStream so login/logout transitions trigger a permission refresh.
EntityService
abstract class EntityService {
String get name;
Future<void> initialize();
void dispose();
}| Member | Type | Description |
|---|---|---|
name | String | Unique service name |
initialize | Future<void> | Called when the plugin initializes |
dispose | void | Called when the plugin disposes |
EntityServicesContainer
class EntityServicesContainer — type-safe container for managing entity services.
| Method | Signature | Description |
|---|---|---|
register<T> | void register<T extends EntityService>(T service) | Register by type |
get<T> | T get<T extends EntityService>() | Get a service (throws if not found) |
tryGet<T> | T? tryGet<T extends EntityService>() | Get a service (null if not found) |
has<T> | bool has<T extends EntityService>() | Whether a service is registered |
initialize | Future<void> initialize() | Initialize all registered services |
dispose | void dispose() | Dispose all services |
all | List<EntityService> | All registered services |
count | int | Number of registered services |
Convenience accessors (EntityServicesAccess)
Defined in vyuh_entity_system:
extension EntityServicesAccess on EntityServicesContainer {
EntityLocalizationService get localization;
EntityNameCacheService get cache;
EntityHelpService get help;
SearchSyncService get searchSync;
SignatureVerificationService get verification;
EntityLifecycleService? get lifecycle; // optional
QueryCacheService get queryCache;
}Defined in vyuh_entity_system_ui (UIServicesAccess):
extension UIServicesAccess on EntityServicesContainer {
ErrorDisplayService get errorDisplay;
}Usage:
final permService = vyuh.entity!.authorizationProvider; // not via services
final queryCache = vyuh.entity!.services.queryCache;
final errorDisplay = vyuh.entity!.services.errorDisplay;No EntityPermissionService
Authorization no longer lives in the services container. Read it directly from vyuh.entity?.authorizationProvider. See Permissions.
Built-in services
EntityNameCacheService
Caches entity names by identifier and ID. Prevents redundant API calls when displaying entity references.
| Method | Signature | Description |
|---|---|---|
get<T> | Future<EntityInfo?> get<T extends EntityBase>(String id) | By Dart type + ID |
getByIdentifier | Future<EntityInfo?> getByIdentifier(String identifier, String id) | By identifier + ID |
getName | Future<String?> getName(String identifier, String? id) | Resolve display name string (single canonical resolver) |
getNameByType<T> | Future<String?> getNameByType<T extends EntityBase>(String? id) | Type-based variant of getName |
invalidate<T> | void invalidate<T extends EntityBase>([String? id]) | Invalidate type (or single ID) |
invalidateByIdentifier | void invalidateByIdentifier(String identifier, [String? id]) | Invalidate by identifier |
invalidateAll | void invalidateAll() | Clear all entries |
cacheSize | int | Total cached entries |
cacheSizeByIdentifier | Map<String, int> | Entries per entity identifier |
EntityInfo is a @JsonSerializable model with id, name, and optional description. The cache deduplicates concurrent in-flight requests for the same (identifier, id) pair.
EntityLocalizationService
Provides localized strings for entity names, fields, actions, and error messages. Supports multiple locales with fallback to English.
| Method | Signature | Description |
|---|---|---|
register<T> | void register<T extends EntityBase>(EntityLocalizationData data) | Register localization data |
setLocale | void setLocale(Locale locale) | Update current locale |
getEntityName<T> | String getEntityName<T extends EntityBase>([String? locale]) | Localized entity name |
getEntityPluralName<T> | String getEntityPluralName<T extends EntityBase>([String? locale]) | Localized plural name |
getFieldLabel<T> | String getFieldLabel<T extends EntityBase>(String fieldName, [...]) | Localized field label |
getFieldHint<T> | String? getFieldHint<T extends EntityBase>(String fieldName, [...]) | Localized field hint |
getFieldError<T> | String getFieldError<T extends EntityBase>(String fieldName, String errorKey, [...]) | Localized error message |
getActionLabel<T> | String getActionLabel<T extends EntityBase>(String actionName, [...]) | Localized action label |
hasLocalization<T> | bool hasLocalization<T extends EntityBase>() | Whether localization exists |
EntityRealtimeService
Manages real-time entity update subscriptions with configurable strategies (polling, SSE).
EntityRealtimeService({RealtimeStrategy? strategy});Default strategy: PollingRealtimeStrategy.
| Method | Signature | Description |
|---|---|---|
subscribe | String subscribe(RealtimeConfig config) | Subscribe to entity changes (returns ID) |
unsubscribe | void unsubscribe(String subscriptionId) | Cancel a subscription |
pauseSubscription | void pauseSubscription(String subscriptionId) | Pause without removing |
resumeSubscription | void resumeSubscription(String subscriptionId) | Resume a paused subscription |
isSubscribed | bool isSubscribed(String subscriptionId) | Whether a subscription is currently active |
activeSubscriptionCount | int | Number of active subscriptions |
final subId = vyuh.entity!.services.get<EntityRealtimeService>().subscribe(
RealtimeConfig(
entityType: 'checklist_usages',
filter: FilterCondition(
field: 'id', operator: FilterOps.equals, value: usageId,
),
onUpdate: () => _refreshData(),
refreshInterval: Duration(seconds: 10),
),
);QueryCacheService
final class QueryCacheService extends EntityService — central facade for stale-while-revalidate query caching. Provides two strategies:
- Lightweight one-shot cache — used by
HttpEntityApi.cachedGet. SimpleMap-based cache with in-flight deduplication. - Reactive observable queries — backed by
cached_query_flutter, exposed asObservableStream<QueryStatus<T>>for MobX widgets.
Both share the same invalidation system (subscribe / announce).
| Method | Signature | Description |
|---|---|---|
getCached<T> | T? getCached<T>(String key, Duration staleDuration) | Read from lightweight cache |
putCached | void putCached(String key, Object? data) | Write to lightweight cache |
getInFlight | Future<Object?>? getInFlight(String key) | Lookup in-flight request |
setInFlight | void setInFlight(String key, Future<Object?> future) | Register in-flight request |
removeInFlight | void removeInFlight(String key) | Clear in-flight entry |
subscribe | void subscribe(String cacheKey, Set<Mutation> mutations) | Register invalidation triggers |
unsubscribe | void unsubscribe(String cacheKey) | Remove invalidation triggers |
announce | void announce(Mutation mutation) | Trigger cache invalidation |
createQuery<T> | Query<T> createQuery<T>({required String key, required Future<T> Function() queryFn, Duration? staleDuration}) | Create or reuse a cached_query_flutter query |
observe<T> | ObservableStream<QueryStatus<T>> observe<T>({...}) | Create query as MobX observable |
invalidateEntity | void invalidateEntity(String entityType) | Invalidate all queries for entity type |
invalidateById | void invalidateById(String entityType, String id) | Invalidate queries for specific entity |
invalidateOperation | void invalidateOperation(String entityType, String operation) | Invalidate by operation |
invalidateByPrefix | void invalidateByPrefix(String prefix) | Invalidate by key prefix |
clearAll | void clearAll() | Clear the entire cache |
Streams
| Property | Type | Description |
|---|---|---|
invalidations | Stream<InvalidationEvent> | Broadcasts all invalidation events |
InvalidationEvent (final class) carries entityType, optional entityId, and optional mutationType.
SignatureVerificationService
Handles e-signature verification for entity operations when required. Only registered when EntitySystemPlugin receives a signatureProvider.
SignatureVerificationService({required SignatureVerificationProvider provider});| Method | Signature | Description |
|---|---|---|
verify | Future<SignatureVerificationResult?> verify({required BuildContext context, required SignatureAction action, ...}) | Show verification dialog (deduplicates concurrent calls) |
saveIcon | IconData saveIcon({required bool requiresVerification}) | Icon for save buttons (shield vs save) |
verify accepts: description, entity / entityResolver / entityCount, role, permissions, requireSignature, showRemarksField, requireRemarks. The optional permissions list is checked through vyuh.entity?.authorizationProvider (Authorize.anyPermission(perms)) before the dialog opens.
EntityHelpService
Manages contextual help content for entity types. Supports asset-based and runtime registration.
| Method | Signature | Description |
|---|---|---|
registerAssetHelp | void registerAssetHelp(String entityType, String assetPath, String packageName) | Register help from asset file |
loadFromAsset | Future<EntityHelpModel?> loadFromAsset(...) | Load and parse help asset |
getHelpModel<T> | Future<EntityHelpModel?> getHelpModel<T extends EntityBase>() | Resolved help model for type |
getHelpModelByIdentifier | Future<EntityHelpModel?> getHelpModelByIdentifier(String entityType) | Resolved help model by identifier |
getEntityHelp<T> | Future<EntityHelp?> getEntityHelp<T extends EntityBase>() | Top-level entity help |
getFieldHelp<T> | Future<FieldHelp?> getFieldHelp<T extends EntityBase>(String fieldName) | Field-level help |
getActionHelp<T> | Future<ActionHelp?> getActionHelp<T extends EntityBase>(String actionName) | Action-level help |
getFAQs<T> | Future<List<FAQ>> getFAQs<T extends EntityBase>() | All FAQs |
searchHelp<T> | Future<List<HelpSearchResult>> searchHelp<T extends EntityBase>(String query) | Fuzzy search |
hasHelp<T> | Future<bool> hasHelp<T extends EntityBase>() | Whether help exists |
preloadHelp<T> | Future<void> preloadHelp<T extends EntityBase>() | Warm the cache |
preloadHelpForEntityTypes | Future<void> preloadHelpForEntityTypes(List<String> entityTypes) | Warm the cache for many types |
clearCache | void clearCache() | Clear cached help data |
The help model is wired to entities via EntityMetadata.getHelp.
SearchSyncService
Syncs entity data to the search index for command palette search.
| Method | Signature | Description |
|---|---|---|
startSyncAndWait | Future<SearchSyncResult> startSyncAndWait() | Start bulk sync and wait for completion |
syncAll | Future<SearchSyncResult> syncAll() | Trigger a full sync |
SearchSyncResult and EntitySyncResult carry per-type sync stats.
EntityDataExtractor
Static utility for extracting search documents from all registered entity configurations. Produces documents for entities, help content, and actions.
| Method | Signature | Description |
|---|---|---|
extractSearchDocuments | static Future<List<Map<String, dynamic>>> extractSearchDocuments() | Extract all search documents |
Document types: entity, help, action.
ErrorDisplayService (UI)
Lives in vyuh_entity_system_ui. Access via vyuh.entity!.services.errorDisplay.
Shows entity errors as banners, dialogs, inline messages, panels, or full pages — picking the appropriate visual based on context. See the error/ package directory for ErrorBanner, ErrorDialogWidget, ErrorInlineWidget, ErrorPanelWidget, ErrorPageWidget, and ErrorSnackbarWidget widgets.
Registration and access patterns
Registering a custom service
class MyAnalyticsService extends EntityService {
@override
String get name => 'my_analytics_service';
@override
Future<void> initialize() async { /* setup */ }
@override
void dispose() { /* cleanup */ }
Future<Map<String, dynamic>> getStats(String entityType) async { /* … */ }
}
// During extension initialization:
await vyuh.entity!.registerService<MyAnalyticsService>(MyAnalyticsService());Accessing services
// Convenience accessors
final queryCache = vyuh.entity!.services.queryCache;
final help = vyuh.entity!.services.help;
final errorDisplay = vyuh.entity!.services.errorDisplay;
// Generic get
final analytics = vyuh.entity!.services.get<MyAnalyticsService>();
// Safe access (null if not registered)
final lifecycle = vyuh.entity!.services.lifecycle;See Also
- EntityConfiguration — Configuration that registers with the plugin
- Permissions —
AuthorizationProviderand theAuthorizeGuardwidget - EntityApi — API that uses
QueryCacheServicefor stale-while-revalidate caching - Glossary — Term definitions