Skip to content

Entity Blueprint

Entity Blueprint is the declarative model for building regulated software from connected entities. It is intentionally portable: pure Dart, JSON-safe, and free of Flutter, server, Supabase, and runtime dependencies.

The goal is to let a domain package describe what the application is before any specific surface is generated or mounted:

  • the business entities and relationships
  • the actional facets that own state and lifecycle
  • the actions users and systems can perform
  • the composable rules and conditions that govern those actions
  • the evidence and audit posture required for regulated execution
  • the DB, API, UI, seed, and app intent required by compilers
  • the module lineage for every contributed item

The same model should be useful for CVS, ELog, WMS, IPQC, BRM, LIMS-like surfaces, batch manufacturing, transport, inventory, and other manufacturing solutions.

Philosophy

Blueprint is not a UI model, a database schema, or a server runtime. It is the shared language underneath all of them.

  1. Business language first. Use entities, facets, actions, policies, evidence, and audit. Avoid implementation suffixes in the domain vocabulary.
  2. Modules compose the application. A module defines entities or contributes descriptors to entities exported by another module.
  3. Entities are stable business types. manufacturing.product is the same product whether CVS, ELog, or IPQC contributes more facets to it.
  4. Facets own actional dimensions. Tenancy, identity, governance, physical profile, cleaning profile, execution history, calibration posture, and assignment are facets.
  5. Actions are always facet-scoped. Actions that feel entity-wide belong to the identity facet. Entity headers, routes, and command palettes may expose those actions, but ownership stays with the facet.
  6. Actions change state. Regulated software should not rely on direct writes when a lifecycle, policy, workflow, evidence, or audit rule applies.
  7. Every action has context. ActionDefinition execution captures who, when, why, what, and where through ActionContext.
  8. Projection is state shape. Facet projection explains how facet state is read from and written to storage/API/client state. Derived values are computed values, not projections.
  9. Server enforcement is authoritative. UI hints control affordances, but server policy checks, lifecycle checks, evidence checks, and audit writes are the enforcement boundary.
  10. Compilers produce surfaces. DB, API, UI, seed, and app compilers consume the same bootstrapped blueprint.
  11. Lineage is first-class. Every effective node can be traced to the module and descriptor that contributed it.

End-to-End Flow

blueprint.bootstrap() is the implicit assembly boundary. Callers define base modules and descriptor modules; bootstrap returns the effective entity graph that compilers, validators, explorers, and runtime adapters consume.

Object Model At A Glance

ModelMeaningPrimary owner
BlueprintComplete application definition.Application package
ModulePackage-level boundary that defines or contributes domain content.Domain package
ModuleExportExplicit export of entities, derived values, or triggers.Module
EntityStable business object and facet composition boundary.Base module
EntityDescriptorContribution to an entity type.Same module or dependency module
FacetActive dimension of an entity.Entity or descriptor
FieldTyped state member.Facet
RelationshipEntity-to-entity connection.Facet
LifecycleFacet-owned state machine.Facet
TransitionAllowed lifecycle move.Lifecycle
ActionDefinitionFacet-scoped transactional action surface.Facet
ActionContextWho, when, why, what, where action envelope.Runtime, declared in blueprint types
RuleDefinitionComposable action rule for access, policy, lifecycle, data, evidence, audit, snapshot, effect, or custom decisions.Action
ConditionDeclarative rule logic vocabulary.Rule
EvidenceRequired or captured proof.Facet/action/runtime
AuditEnvelopeRegulated audit posture.Facet/action/runtime
DerivedValueComputed value from DB or server.Facet/entity/module
FacetProjectionFacet-owned state projection.Facet
EntityStateProjectionUnion of all facet projections.Bootstrapped entity
EffectDownstream action emitted by transitions or actions.Facet/action
TriggerRuntime trigger descriptor.Module
SubscriptionCross-facet or cross-entity reaction declaration.Facet/module
ModuleDb / EntityDb / FieldDbDB and performance hints.Module/entity/field
ModuleUi / EntityUi / FacetUi / FieldUi / ActionUiFlutter-free UI hints.Module/entity/facet/field/action
ModuleSeed / EntitySeed / FieldSeedSeed generation hints.Module/entity/field
DartReferenceStatic code reference without runtime dependency.Any model that delegates logic
EntityOriginGraphLineage for every effective node.Bootstrapper

Application and Module Composition

An application blueprint is made from modules. A module can define entities, declare policies, expose DB/UI/seed hints, export reusable items, and contribute descriptors to entities defined elsewhere.

dart
final blueprint = Blueprint(
  name: 'manufacturing_super_app',
  version: '1.0.0',
  modules: [
    manufacturingModule,
    iamModule,
    cvsModule,
    elogModule,
  ],
);

final bootstrapped = blueprint.bootstrap();
final product = bootstrapped.entity('manufacturing.product');

The base manufacturing module defines shared entities such as product, equipment, area, material, method, and equipment train. CVS and ELog should not create competing product types. They contribute descriptors to manufacturing.product.

Entity

An entity is a stable business object and composition boundary. It carries a schema type, title, description, facets, aggregate projections, DB hints, UI hints, and seed hints. It does not own actions directly.

Examples:

  • manufacturing.product
  • manufacturing.equipment
  • manufacturing.area
  • iam.user
  • iam.user_group
  • iam.role
  • cvs.protocol
  • elog.logbook

The entity is the unit users think about, APIs route around, tables list, and permissions often target. The facet is the unit that owns state dimensions and behavior. If an action feels entity-wide, model it on the identity facet.

Entity Descriptor

An EntityDescriptor contributes additional dimensions to an entity. It is the mechanism that lets modules layer into the same stable type.

text
manufacturing.product
  identity
  tenancy
  governance
  composition

cvs descriptor -> manufacturing.product
  cleaning profile
  MACO participation
  protocol impact

elog descriptor -> manufacturing.product
  execution history
  logbook applicability

After bootstrap, the effective entity is one product:

text
manufacturing.product
  identity
  tenancy
  governance
  composition
  cleaning profile
  MACO participation
  protocol impact
  execution history
  logbook applicability

Descriptors may contribute new facets or merge into an existing facet. Shared facets such as governance and tenancy are expected to accept contributions from many modules.

Facet

A facet is an actional dimension of an entity. It is the main extensibility unit in the blueprint.

A facet can contain:

  • title and description
  • fields
  • relationships
  • lifecycle
  • actions
  • rules and conditions
  • evidence declarations
  • audit envelope
  • derived values
  • subscriptions and effects
  • facet state projection
  • UI hints

This lets the blueprint say that an equipment entity has several independent regulated dimensions:

text
equipment
  identity
  tenancy
  governance
  physical profile
  cleaning status
  calibration posture
  maintenance posture
  execution usage

Not every facet needs a lifecycle. Some facets are mostly state and relationships. Others are actional and define transitions, actions, evidence, rules, and conditions.

Fields and Field Types

Fields are typed members owned by facets. Titles and descriptions are core metadata, not UI-only data, because DB, API, UI, docs, localization, seed, and analytics all need human-readable semantics.

The type model is portable and maps cleanly to Postgres, API schemas, and UI widgets:

  • text, integer, bigint, double, decimal
  • boolean
  • date, timestamp, interval
  • UUID
  • JSONB
  • enum
  • decision

Fields may carry DB hints, UI hints, and seed hints. For example, a field can declare that it is indexed, commonly filtered, visible in a table, editable only under a policy, or seeded from a regulated synthetic distribution.

Relationships

Relationships declare how entities connect:

  • belongsTo
  • hasOne
  • hasMany
  • manyToMany

They also declare cascade rules, embed hints, join semantics, and lineage. This is where manufacturing graph structure starts: products use equipment trains, equipment belongs to areas, sampling locations belong to equipment, protocols reference methods, and assignments bind actors to scoped resources.

Lifecycle and Transitions

Lifecycle belongs inside a facet. A single entity can have many state dimensions, but each dimension should have one clear owner.

Examples:

text
equipment.cleaning_status
  dirty -> cleaned -> sampled -> released

protocol.approval
  draft -> reviewed -> approved -> effective -> superseded

material.quality_status
  quarantine -> released -> blocked

Transitions reference rules and declare effects. When a workflow is required, the transition is still the business state change; the workflow is the orchestration used to reach it.

Actions and ActionContext

An action is the transactional surface of a facet. It may create, update, approve, release, calculate, assign, sample, review, reject, rework, or retire state. The owning facet represents the intent:

  • identity owns create, publish, archive, restore, purge, and other actions that establish or retire the entity record.
  • assignment owns assign, reassign, delegate, claim, and release assignment.
  • lifecycle or execution owns start, pause, resume, submit, complete, and cancel operational work.
  • evidence owns capture, replace, verify, and reject proof.
  • policy_snapshot owns resolve, refresh, freeze, and explain effective policy.
  • exception owns raise, triage, remedy, clear, and reopen exceptions.

Actions declare:

  • payload fields
  • rules
  • evidence requirements
  • audit envelope
  • effects and triggers
  • UI hints

Rules are the complete declarative decision surface for an action. Use RuleKind to name the concern and RulePhase to name when it runs:

  • RuleKind.access with ActorHasGrantCondition for grants and permissions.
  • RuleKind.policy with PolicyAllowsCondition for effective policy checks.
  • RuleKind.lifecycle with StateIsCondition or an evaluator for state moves.
  • RuleKind.data with field/path conditions or an evaluator for payload and entity invariants.
  • RuleKind.evidence, audit, snapshot, and effect for regulated proof, capture, and downstream work.
  • RulePhase.availability, beforeCommit, afterCommit, and async to place a rule in the transaction timeline.

Conditions are the vocabulary of logic. AllCondition, AnyCondition, and NotCondition compose smaller checks. Built-in conditions cover common entity work, while EvaluatorCondition gives runtimes a stable hook for logic that must be implemented in Postgres, the policy engine, or application code.

At execution time, ActionContext captures:

  • who: actor, roles, groups, delegation, signature principal
  • when: timestamp and clock source
  • why: reason, justification, change control, deviation, or ticket
  • what: action, payload, target entity, previous state, intended state
  • where: tenant, site, area, workstation, device, session, IP, correlation ID

The blueprint declares what must be captured. The runtime captures, validates, and persists it.

Runtime Execution on Postgres

Blueprint execution should compile to a Postgres-backed transaction contract. The runtime owns execution, but the declaration should be specific enough that Postgres can enforce and explain the important invariants.

A facet action execution should generally follow this shape:

  1. Resolve the bootstrapped entity, owning facet, action, lifecycle, and projection metadata.
  2. Authenticate the actor and build an ActionContext with tenant/site, device, request, correlation, reason, and signature metadata.
  3. Open one Postgres transaction.
  4. Lock the target host/facet rows with SELECT ... FOR UPDATE.
  5. Evaluate availability and before-commit rules, including access grants, effective policy decisions, lifecycle state, typed facet state, related entities, generated views, policy reads, evidence, and action payload.
  6. Capture required snapshots, including policy/config versions and relevant master/entity values.
  7. Apply lifecycle and data changes to the owning facet and any declared write targets.
  8. Insert immutable action transaction, audit, evidence, and decision-trace rows.
  9. Insert outbox/effect rows for downstream facet actions, workflow tasks, realtime notifications, projections, and analytics work.
  10. Commit, then let outbox runners process eventual effects idempotently.

Postgres concepts are central to that runtime:

  • facet tables keep state normalized and lockable
  • generated columns and check constraints encode cheap invariants
  • foreign keys and audited join tables encode entity relationships
  • RLS and security-definer/invoker functions protect scoped access
  • transaction tables hold canonical action attempts and outcomes
  • audit/evidence tables preserve immutable regulated history
  • outbox tables bridge strict commits to eventual cross-facet effects
  • views/materialized views/projected read models keep API/UI queries efficient
  • advisory locks or idempotency keys protect high-contention actions

The portable blueprint types do not execute any of this. They declare the contract that lets compilers and runtime adapters produce it consistently.

Rules, Access, and Policy

Blueprint types do not define policy objects. Policy objects, domains, sections, parameters, bindings, merge strategies, and effective policy resolution belong to vyuh_policy_engine.

Blueprints reference policies through RuleDefinition values with RuleKind.policy and PolicyAllowsCondition. Access uses the same mechanism: RuleKind.access and ActorHasGrantCondition. Keeping access and policy as rule kinds gives them distinct vocabulary without splitting action logic into separate precondition, postcondition, guard, access, and policy arrays.

Policy rules are not state projections. They are named decisions computed from policy, context, state, and reads.

Evidence and Audit

Evidence and audit are related but not identical.

Evidence is proof. It can be a checklist, signature, reason, artifact, photo, instrument file, log excerpt, training acknowledgement, external reference, or other record.

Audit is the immutable history and compliance envelope. It records the action, actor, context, previous state, new state, evidence links, policy decisions, exceptions, signatures, and runtime trace.

In GMP/ALCOA+ terms, the runtime must make actions:

  • attributable
  • legible
  • contemporaneous
  • original
  • accurate
  • complete
  • consistent
  • enduring
  • available

Blueprint declares evidence requirements and audit posture. Runtime packages capture and enforce the actual records.

Derived Values

DerivedValue represents computed values. It supports several tiers:

  • generated column
  • SQL view
  • materialized view
  • server evaluator
  • server evaluator

Derived values are useful for MACO calculations, equipment surface summaries, readiness scores, exception counts, overdue-review flags, search labels, effective status, and impact summaries.

Use a derived value when the value is computed from other facts. Use a facet projection when describing how facet state is exposed, flattened, indexed, read, and written.

Facet Projection and Entity State Projection

Facets own state, so facets also own projection.

FacetProjection describes how the state inside one facet is exposed:

  • which field path is projected
  • whether it is read-only or read-write
  • whether it is stored inline, in a facet table, in JSONB, in a view, or in a derived source
  • how it should behave for filtering, sorting, search, grouping, and aggregation
  • whether it is visible or editable through generated UI surfaces

EntityStateProjection is the union of all facet projections for an effective entity. It provides the bridge between normalized facet storage and simple client-side state.

text
Facet state
  identity.code
  identity.title
  tenancy.tenant_id
  tenancy.site_id
  physical_profile.surface_area_cm2
  cleaning_status.current_state

Entity state projection
  code
  title
  tenant_id
  site_id
  surface_area_cm2
  current_cleaning_state

The runtime and compilers can preserve facet boundaries internally while giving client code a simple property surface:

dart
entity.property('surface_area_cm2');
entity.property('current_cleaning_state');

The projection model also gives the DB compiler enough intent to create typed columns, indexes, JSONB columns, views, and read models without forcing the UI or API to understand every storage split.

Supabase/Postgres Mapping

The default storage strategy is relational and facet-aware:

  • the identity facet becomes the host table
  • non-identity facets become facet tables
  • facet tables use entity_id as primary key and foreign key to the host table
  • fields become typed columns
  • lifecycle state fields become typed columns on the owning facet table
  • relationships become FKs or join tables
  • audit rows are written to audit tables
  • evidence rows link back to action/audit records
  • derived values become generated columns, SQL views, materialized views, or server-evaluated read fields
  • the full read model joins identity plus facet tables into a stable API shape

Performance intent is declared through DB hints:

  • module-level schemas, RLS, realtime, outbox, triggers, cross-entity indexes, and views
  • entity-level row scale, write rate, partitioning, retention, audit mode, and realtime posture
  • field-level indexes, index kind, cardinality, query patterns, uniqueness, and nullable posture

This lets manufacturing apps stay normalized and indexable while still exposing a coherent entity model to APIs and clients.

UI Mapping

Blueprint UI hints are Flutter-free. They describe what should be generated, not how Flutter renders it.

UI hints can express:

  • menu and route visibility
  • list columns
  • form sections
  • field widgets
  • field visibility and editability
  • action placement
  • action availability
  • evidence prompts
  • signature and reason prompts
  • redaction, hide, disable, placeholder, and read-only fallbacks
  • icons and categories through portable references

The UI compiler turns these hints into Entity System configuration. Entity System UI renders the final Flutter surface. The server remains authoritative for enforcement.

Seed Mapping

Seed hints describe the data a module needs for smoke tests, demos, validation, and load scenarios. They are separate from DB hints because seeds can be served through SQL, API fixtures, generated JSON, AI-generated packs, or scenario scripts.

Seed hints can express:

  • dataset size
  • realism level
  • required entities and relationships
  • field value strategy
  • distributions and examples
  • uniqueness and reference behavior
  • validation scenario intent

Origin Graph

Every effective node has lineage.

The origin graph records:

  • source module
  • descriptor source
  • aspect
  • parent path
  • contributed item
  • merge target
  • effective path

This is important for regulated manufacturing because a user should be able to ask:

  • Which module added this field?
  • Which descriptor contributed this policy requirement?
  • Which facet owns this lifecycle transition?
  • Which module changed the evidence requirement?
  • Why does this entity have this DB index?
  • Which package made this UI action visible?

The origin graph supports blueprint explorer views, diagnostics, impact analysis, compiler reports, and governance reviews.

Compiler Responsibilities

Blueprint is the declarative input. Compilers turn it into usable surfaces.

CompilerOutput
DB compilerSupabase/Postgres schemas, tables, joins, RLS, triggers, indexes, views, audit/evidence tables, outbox plans
API compilerroute plans, payload schemas, OpenAPI, action endpoints, policy checkpoints, error contracts
UI compilerEntity System configuration, routes, forms, tables, actions, evidence prompts, signature prompts
Seed compilerSQL, JSON, scenario packs, AI seed hints, load data plans
App compilerVyuh app scaffold, feature registration, plugin setup, route registration, demo runtime wiring

Compilers should consume blueprint.bootstrap().assembledEntities, not raw base entities. That ensures descriptor-contributed facets participate in the same DB/API/UI/seed pipeline as base module facets.

Runtime Boundary

Blueprint describes. Runtime enforces.

The runtime owns:

  • authenticated actors
  • IAM principals, roles, groups, permissions, grants, assignments, and scopes
  • effective policy resolution and policy evaluation
  • action execution
  • lifecycle enforcement
  • evidence capture
  • audit writes
  • exception and remedy handling
  • realtime events and subscriptions
  • storage adapters
  • telemetry and correlation

The client may render disabled or hidden actions, but every mutation must be validated on the server with the current actor, scope, entity state, lifecycle, policy, evidence, and action context.

Manufacturing Layering Example

text
manufacturing module
  product
    identity
    tenancy
    governance
    composition
  equipment
    identity
    tenancy
    governance
    physical profile
  area
    identity
    tenancy
    hierarchy

cvs module
  descriptor -> manufacturing.product
    cleaning profile
    MACO participation
  descriptor -> manufacturing.equipment
    cleaning status
    sampling locations
  entities
    protocol
    assessment
    result

elog module
  descriptor -> manufacturing.product
    execution history
  descriptor -> manufacturing.equipment
    usage history
  entities
    logbook
    log entry

The result is not three disconnected applications. It is one effective graph where shared manufacturing entities accumulate regulated dimensions from each solution package.

Imperative First, Annotations Later

The first implementation path is imperative:

dart
final cleaningProfile = Facet(
  name: 'cleaning_profile',
  title: 'Cleaning Profile',
  fields: [
    Field(
      name: 'worst_case_rank',
      title: 'Worst Case Rank',
      type: IntegerType(),
    ),
  ],
);

Annotations and generators should emit the same model. They are a convenience for authoring, not a separate source of truth.

Current Gaps and Extension Points

The core model is broad enough to express manufacturing applications, but the runtime and compiler layers still need to harden around:

  • IAM package and runtime enforcement
  • policy engine extraction and scoped policy resolution
  • DB compiler output for RLS, triggers, audit, evidence, and facet projections
  • API compiler output for action endpoints and OpenAPI
  • UI compiler output into Entity System configuration
  • exception/remedy registry
  • localization, icon, category, evaluator, and validator registries
  • SOP/template extraction into blueprint descriptors
  • impact graph traversal and query services
  • realtime action stream and outbox processing
  • regulated test/demo environment generation

Those gaps are intentional package boundaries. They should be implemented by runtime, compiler, server, UI, and domain packages rather than pushed into the portable blueprint type package.

Blue is the Vyuh Blueprint documentation surface.