Skip to content

EntityBase

The foundational class for all entities in the Vyuh Entity System. Every business object managed by the system extends EntityBase.

Class Signature

Properties

PropertyTypeDefaultDescription
idString--Unique identifier for the entity (read via readFieldValue)
nameString''Display name (read via readFieldValue, falls back to title)

Display Getters

PropertyTypeDefaultDescription
displayTitleStringreturns nameOverride to provide a custom title
displaySubtitleStringreturns ''Override to provide a subtitle
displayPropertiesMap<String, String>returns {}Key-value pairs for compact display contexts (mobile rows, related-entity cards, search results)

Abstract Methods

  • toJson
    Map<String, dynamic> toJson()

    Serialize the entity to JSON. Every concrete entity must implement this — typically generated by json_serializable with build_runner.

EntityBase extends ContentItem from vyuh_core, inheriting the content-item infrastructure (schemaType, layout, modifiers).

id and name use @JsonKey(readValue: readFieldValue) so they support configurable JSON key names via FieldKeyMap.

Audit, version, draft fields are mixin-based

EntityBase only carries identity (id, name). createdAt / updatedAt / createdBy / updatedBy come from the Auditable mixin. Versioning fields come from Versionable. Draft state comes from Draftable. The vyuh_entity_generator produces a $EntityBase superclass that mixes in the appropriate capabilities with their concrete field declarations.

Overriding Display Getters

dart
class Equipment extends EntityBase {
  final String code;
  final String location;

  @override
  String get displayTitle => '$code - $name';

  @override
  String get displaySubtitle => location;

  @override
  Map<String, String> get displayProperties => {
    'Code': code,
    'Location': location,
  };
}

FieldKey Enum

Defines the standard field names for entity properties — used by readFieldValue and FieldKeyMap to map camelCase Dart fields to configurable JSON keys.

dart
enum FieldKey {
  // Identity
  id,
  name,
  title,
  description,

  // Auditable
  createdAt,
  updatedAt,
  createdBy,
  updatedBy,

  // Versionable
  versionNumber,
  isActive,
}

FieldKeyMap

A final class static mapper that converts FieldKey values to their JSON/database column name. The default mapping uses snake_case:

FieldKeyDefault JSON Key
id'id'
name'name'
title'title'
description'description'
createdAt'created_at'
updatedAt'updated_at'
createdBy'created_by'
updatedBy'updated_by'
versionNumber'version_number'
isActive'is_active'

Customizing the Key Map

Override the key map when your backend uses different column names:

dart
FieldKeyMap.keyMap = {
  FieldKey.createdAt: 'created_date',
  FieldKey.updatedAt: 'modified_date',
};

Unmapped keys fall back to the defaults.

Methods

MethodSignatureDescription
keystatic String key(FieldKey field)Returns the JSON key for a field
keyMap (setter)static set keyMap(Map<FieldKey, String> keyMap)Overrides key mappings

readFieldValue

Top-level helper used as the readValue callback in @JsonKey annotations on entity fields:

dart
Object? readFieldValue(Map<dynamic, dynamic> json, String key);

Reads a value by trying:

  1. The camelCase key directly (json['createdAt'])
  2. The configured snake_case column name via FieldKeyMap

Special case: name falls back to title for backward compatibility.

Entity Mixins

Mixins provide type-safe access to common properties without relying on JSON conversion or reflection.

Auditable

dart
mixin Auditable on EntityBase {
  DateTime? get createdAt;
  DateTime? get updatedAt;
  String? get createdBy;
  String? get updatedBy;
}

Members are abstract — for hand-written entities, declare the fields with @override and @JsonKey(readValue: readFieldValue) in the concrete class. The generator emits concrete declarations in the $EntityBase superclass.

HasCode

dart
mixin HasCode {
  String get code;
}

HasDescription

dart
mixin HasDescription {
  String? get description;
}

HasActiveStatus

dart
mixin HasActiveStatus {
  bool get isActive;
}

HasStatus

dart
mixin HasStatus {
  String get status;
}

CommonEntityProperties

A convenience mixin combining HasCode, HasDescription, and HasActiveStatus:

dart
mixin CommonEntityProperties implements HasCode, HasDescription, HasActiveStatus {}

Versionable

Adds version tracking (lives in versioning_audit/models/versioned_entity.dart):

dart
mixin Versionable on EntityBase {
  int get versionNumber;
  bool get isActive;
}

Draftable

Adds draft support to versionable entities (lives in drafts/draftable.dart):

dart
mixin Draftable on Versionable {
  DraftMetadata? get draftMetadata;

  bool get isEntityDraft;
  bool get hasModificationDraft;
  bool get showDraftIndicator;
}

Extension: DraftStatusAccess

dart
extension DraftStatusAccess on EntityBase {
  bool get isDraft;
}

Returns true if the entity implements Draftable and showDraftIndicator is true.

Extension: EntityPropertyAccess

dart
extension EntityPropertyAccess<T extends EntityBase> on T {
  String? get codeIfSupported;
  String? get descriptionIfSupported;
  bool? get isActiveIfSupported;
  String? get statusIfSupported;
}

Safe accessors that return null when the entity does not implement the corresponding mixin.

EntityReference

A lightweight reference to another entity, used for relationships.

dart
@JsonSerializable(createToJson: true)
class EntityReference {
  final String id;
  final String name;
  final String? description;

  const EntityReference({
    required this.id,
    required this.name,
    this.description,
  });

  factory EntityReference.fromJson(Map<String, dynamic> json);
  Map<String, dynamic> toJson();
}
PropertyTypeDescription
idStringUnique identifier of the referenced entity
nameStringDisplay name of the referenced entity
descriptionString?Optional description

Equality is based on id only — two references to the same entity are equal regardless of name changes.

Usage Example

dart
@JsonSerializable(fieldRename: FieldRename.snake)
class Area extends EntityBase
    with Auditable, Versionable, Draftable, HasCode {
  @override
  final String code;
  final String location;
  @override
  final bool isActive;
  @override
  final int versionNumber;

  // Auditable
  @override
  @JsonKey(readValue: readFieldValue)
  final DateTime? createdAt;
  @override
  @JsonKey(readValue: readFieldValue)
  final DateTime? updatedAt;
  @override
  @JsonKey(readValue: readFieldValue)
  final String? createdBy;
  @override
  @JsonKey(readValue: readFieldValue)
  final String? updatedBy;

  // Draftable
  @override
  DraftMetadata? draftMetadata;

  Area({
    required super.id,
    required super.name,
    required this.code,
    required this.location,
    this.isActive = true,
    this.versionNumber = 1,
    this.createdAt,
    this.updatedAt,
    this.createdBy,
    this.updatedBy,
    this.draftMetadata,
  }) : super(
         schemaType: 'lms.area',
         layout: null,
         modifiers: null,
       );

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

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

  @override
  String get displayTitle => '$code - $name';

  @override
  String get displaySubtitle => location;
}

In practice, these Auditable / Versionable / Draftable field declarations are emitted by the code generator into a $EntityBase superclass — your hand-written entity only needs to declare its domain-specific fields and mix in the capabilities.

See Also