Skip to content

Vyuh Entity System

Entity Management Framework

A type-safe, configuration-driven framework for managing business entities in Flutter applications. Define entities once with full CRUD, permissions, drafts, versioning, audit trails, and configurable UI layouts.

Why Entity System?

Configuration-Driven Entities

Define metadata, API, layouts, editors, actions, and routing in a single EntityConfiguration descriptor per entity type.

Authorization-Aware by Default

Declarative gating via the Authorize DSL on routes, layouts, actions, and tabs. Backed by a pluggable AuthorizationProvider with permissions, roles, and combinator expressions.

Drafts, Versioning & Audit

EntityCapability mixins (Auditable, Versionable, Draftable) layer in audit fields, version history, and the full draft approval workflow with no boilerplate.

Configurable Layouts & Editors

Table, grid, and custom collection layouts; tabbed detail layouts; per-entity dashboards and analytics. Form-based or multi-part editors with optional e-signature gates.

Getting Started

Core Concepts

Key Components

ComponentDescription
EntityConfiguration<T>Central descriptor unifying metadata, API, layouts, editors, actions, fields, and routing
EntityBaseBase class for all entities (id, name) with capability mixins for Auditable / Versionable / Draftable fields
EntityApi<T> / HttpEntityApi<T>Abstract CRUD contract with the standard HTTP implementation, EndpointBuilder, and query caching
EntityLayouts<T>Holds list, detail, summary, dashboard, analytics, and item/comparison layouts
EntityEditor<T>Editor configuration: form-based, multi-part, signature-driven, with transformers and lifecycle resolvers
EntityRouting<T>Path patterns (NavigationPathBuilder), the route builder, navigation mode, and per-route Authorize gates
EntitySystemPluginVyuh plugin that registers entity configurations, generates GoRouter routes, and owns the AuthorizationProvider
EntityExtensionDescriptorPer-feature descriptor for entities, services, field formatters, and hierarchies

Learn More

Quick Example

Define a Course entity for a Learning Management System. The usual path is annotation-driven: the generator emits the base class, fields registry, API, layouts, editor, routes, and courseConfig descriptor.

dart
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:vyuh_entity_annotations/vyuh_entity_annotations.dart';
import 'package:vyuh_entity_system_ui/vyuh_entity_system_ui.dart';

part 'course.g.dart';
part 'course.entity.dart';

@Entity(
  schema: 'lms',
  identifier: 'courses',
  name: 'Course',
  pluralName: 'Courses',
  description: 'Training courses for participants',
  icon: FluentIcons.book_24_regular,
  visibility: EntityVisibility.all,
  navigation: EntityNavigation(
    prefix: '/lms/courses',
    authorize: AuthorizeEntity(
      list: AuthorizeRule(Authorize.permission('lms.courses.view')),
      create: AuthorizeRule(Authorize.permission('lms.courses.create')),
      view: AuthorizeRule(Authorize.permission('lms.courses.view')),
      edit: AuthorizeRule(Authorize.permission('lms.courses.update')),
    ),
  ),
  layouts: EntityUILayouts(
    table: TableLayout(
      columns: [
        TableColumn('name', widthFactor: 0.35, minWidth: 200),
        TableColumn('level', widthFactor: 0.2),
        TableColumn('status', widthFactor: 0.2),
        TableColumn('durationMinutes', widthFactor: 0.15),
      ],
    ),
  ),
)
@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false)
class Course extends $CourseBase {
  @Field(label: 'Description', type: TextFieldType(lines: 3))
  final String? description;

  @Field(label: 'Level', filterable: true, sortable: true)
  final String level;            // beginner | intermediate | advanced

  @Field(label: 'Status', filterable: true, sortable: true)
  final String status;           // draft | published | archived

  @Field(label: 'Duration (min)', sortable: true)
  final int durationMinutes;

  Course({
    required super.id,
    required super.name,
    this.description,
    this.level = 'beginner',
    this.status = 'draft',
    this.durationMinutes = 0,
    super.versionNumber,
    super.isActive,
    super.createdAt,
    super.updatedAt,
    super.createdBy,
    super.updatedBy,
  });

  factory Course.fromJson(Map<String, dynamic> json) => _$CourseFromJson(json);

  @override
  Map<String, dynamic> toJson() => _$CourseToJson(this);
}

// Register the generated `courseConfig` in an EntityExtensionDescriptor.

See the Quick Start for the full step-by-step.