Skip to content

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

dart
EntitySystemPlugin({
  required this.baseUrl,
  AuthorizationProvider? authorizationProvider,
  this.signatureProvider,
  this.filterWidgetDelegate,
});
ParameterTypeDefaultDescription
baseUrlString--Base URL for all API endpoints
authorizationProviderAuthorizationProvider?OpenAuthorizationProvider()Authorization implementation (synchronous after refresh)
signatureProviderSignatureVerificationProvider?nullE-signature verification provider
filterWidgetDelegateFilterWidgetDelegate?nullOptional delegate for filter UI (typically supplied by cdx_feature_query)

Properties

PropertyTypeDescription
baseUrlStringAPI base URL
authorizationProviderAuthorizationProviderActive authorization provider
signatureProviderSignatureVerificationProvider?Signature verification provider
filterWidgetDelegateFilterWidgetDelegate?Filter UI delegate
servicesEntityServicesContainerType-safe service container
routeObserverRouteObserver<PageRoute>Navigation observer
registeredTypesList<Type>All registered entity Dart types
registeredIdentifiersList<String>All registered entity identifiers (incl. alternates)
registeredConfigsList<EntityConfiguration<EntityBase>>All configs sorted by menu category, priority, name
configsWithMetadataMap<EntityConfiguration<EntityBase>, EntityMetadata>Configs mapped to metadata

Registration

MethodSignatureDescription
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
unregisterByIdentifierbool unregisterByIdentifier(String identifier)Unregister by identifier
clearvoid clear()Unregister all entities

Retrieval

MethodSignatureDescription
getConfig<T>EntityConfiguration<T>? getConfig<T extends EntityBase>()By Dart type (preserves generic)
getConfigByIdentifierEntityConfiguration<EntityBase>? getConfigByIdentifier(String id)By identifier (type erased)
isRegistered<T>bool isRegistered<T extends EntityBase>()Whether type is registered
isIdentifierRegisteredbool isIdentifierRegistered(String identifier)Whether identifier is registered
getMetadata<T>EntityMetadata? getMetadata<T extends EntityBase>()Metadata for a type
getRegistrationSummaryMap<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

dart
List<RouteBase> generateRoutes();

Generates GoRouter routes for every registered entity whose metadata has hasRoutes == true (i.e. visibility contains RouteVisibility or MenuVisibility).

Service registration

dart
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

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

  1. EntityLocalizationService
  2. EntityNameCacheService
  3. EntityHelpService
  4. SearchSyncService
  5. EntityRealtimeService (with PollingRealtimeStrategy by default)
  6. QueryCacheService
  7. SignatureVerificationService (only when signatureProvider is supplied)

It also binds vyuh.auth.userChanges to authorizationProvider.bindRefreshStream so login/logout transitions trigger a permission refresh.


EntityService

dart
abstract class EntityService {
  String get name;
  Future<void> initialize();
  void dispose();
}
MemberTypeDescription
nameStringUnique service name
initializeFuture<void>Called when the plugin initializes
disposevoidCalled when the plugin disposes

EntityServicesContainer

class EntityServicesContainer — type-safe container for managing entity services.

MethodSignatureDescription
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
initializeFuture<void> initialize()Initialize all registered services
disposevoid dispose()Dispose all services
allList<EntityService>All registered services
countintNumber of registered services

Convenience accessors (EntityServicesAccess)

Defined in vyuh_entity_system:

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

dart
extension UIServicesAccess on EntityServicesContainer {
  ErrorDisplayService get errorDisplay;
}

Usage:

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

MethodSignatureDescription
get<T>Future<EntityInfo?> get<T extends EntityBase>(String id)By Dart type + ID
getByIdentifierFuture<EntityInfo?> getByIdentifier(String identifier, String id)By identifier + ID
getNameFuture<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)
invalidateByIdentifiervoid invalidateByIdentifier(String identifier, [String? id])Invalidate by identifier
invalidateAllvoid invalidateAll()Clear all entries
cacheSizeintTotal cached entries
cacheSizeByIdentifierMap<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.

MethodSignatureDescription
register<T>void register<T extends EntityBase>(EntityLocalizationData data)Register localization data
setLocalevoid 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).

dart
EntityRealtimeService({RealtimeStrategy? strategy});

Default strategy: PollingRealtimeStrategy.

MethodSignatureDescription
subscribeString subscribe(RealtimeConfig config)Subscribe to entity changes (returns ID)
unsubscribevoid unsubscribe(String subscriptionId)Cancel a subscription
pauseSubscriptionvoid pauseSubscription(String subscriptionId)Pause without removing
resumeSubscriptionvoid resumeSubscription(String subscriptionId)Resume a paused subscription
isSubscribedbool isSubscribed(String subscriptionId)Whether a subscription is currently active
activeSubscriptionCountintNumber of active subscriptions
dart
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:

  1. Lightweight one-shot cache — used by HttpEntityApi.cachedGet. Simple Map-based cache with in-flight deduplication.
  2. Reactive observable queries — backed by cached_query_flutter, exposed as ObservableStream<QueryStatus<T>> for MobX widgets.

Both share the same invalidation system (subscribe / announce).

MethodSignatureDescription
getCached<T>T? getCached<T>(String key, Duration staleDuration)Read from lightweight cache
putCachedvoid putCached(String key, Object? data)Write to lightweight cache
getInFlightFuture<Object?>? getInFlight(String key)Lookup in-flight request
setInFlightvoid setInFlight(String key, Future<Object?> future)Register in-flight request
removeInFlightvoid removeInFlight(String key)Clear in-flight entry
subscribevoid subscribe(String cacheKey, Set<Mutation> mutations)Register invalidation triggers
unsubscribevoid unsubscribe(String cacheKey)Remove invalidation triggers
announcevoid 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
invalidateEntityvoid invalidateEntity(String entityType)Invalidate all queries for entity type
invalidateByIdvoid invalidateById(String entityType, String id)Invalidate queries for specific entity
invalidateOperationvoid invalidateOperation(String entityType, String operation)Invalidate by operation
invalidateByPrefixvoid invalidateByPrefix(String prefix)Invalidate by key prefix
clearAllvoid clearAll()Clear the entire cache

Streams

PropertyTypeDescription
invalidationsStream<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.

dart
SignatureVerificationService({required SignatureVerificationProvider provider});
MethodSignatureDescription
verifyFuture<SignatureVerificationResult?> verify({required BuildContext context, required SignatureAction action, ...})Show verification dialog (deduplicates concurrent calls)
saveIconIconData 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.

MethodSignatureDescription
registerAssetHelpvoid registerAssetHelp(String entityType, String assetPath, String packageName)Register help from asset file
loadFromAssetFuture<EntityHelpModel?> loadFromAsset(...)Load and parse help asset
getHelpModel<T>Future<EntityHelpModel?> getHelpModel<T extends EntityBase>()Resolved help model for type
getHelpModelByIdentifierFuture<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
preloadHelpForEntityTypesFuture<void> preloadHelpForEntityTypes(List<String> entityTypes)Warm the cache for many types
clearCachevoid 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.

MethodSignatureDescription
startSyncAndWaitFuture<SearchSyncResult> startSyncAndWait()Start bulk sync and wait for completion
syncAllFuture<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.

MethodSignatureDescription
extractSearchDocumentsstatic 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

dart
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

dart
// 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
  • PermissionsAuthorizationProvider and the AuthorizeGuard widget
  • EntityApi — API that uses QueryCacheService for stale-while-revalidate caching
  • Glossary — Term definitions