System App Migration Lifecycle

How fixed system application schemas are generated, upgraded, and tracked at runtime.

This page documents the runtime lifecycle that generates, upgrades, and tracks database schemas for the four fixed system applications: admin, profiles, metahubs, and applications.

Overview

Each fixed system app declares its target schema shape in a TypeScript manifest (SystemAppDefinition). At server startup the platform compares the manifest against the last recorded migration snapshot in the database and applies only the necessary DDL changes. This is the same diff engine used by user-created applications published through metahubs.

Key Components

Component
Package
Role

SystemAppDefinition

@universo/migrations-core

Type contract for system app manifests

systemAppSchemaCompiler

@universo/migrations-platform

Builds entities from manifest, runs diff

SchemaGenerator

@universo/schema-ddl

Creates schemas, tables, system metadata

SchemaMigrator

@universo/schema-ddl

Calculates and applies incremental diffs

MigrationManager

@universo/schema-ddl

Records migrations in _app_migrations

buildSchemaSnapshot

@universo/schema-ddl

Builds a structural snapshot from entities

calculateSchemaDiff

@universo/schema-ddl

Computes change set between two snapshots

Manifest Structure

A SystemAppDefinition contains:

  • key — unique identifier (e.g. admin).

  • schemaTarget — fixed schema name (e.g. { kind: 'fixed', schemaName: 'admin' }).

  • currentStorageModel / targetStorageModel'application_like' for converged apps.

  • currentBusinessTables / targetBusinessTables — arrays of table definitions with fields, data types, FK references, and defaults.

  • currentStructureCapabilities / targetStructureCapabilities — flags for system tables (_app_objects, _app_attributes, _app_migrations, etc.).

  • migrations — SQL migration entries with bootstrapPhase markers.

Bootstrap Sequence

The full bootstrap runs inside initDatabase() in @universo/core-backend:

Diff Engine

The diff engine supports the following change types:

Change Type
Direction
Example

ADD_TABLE

Additive

New business table added

DROP_TABLE

Destructive

Business table removed

ADD_COLUMN

Additive

New field added to a table

DROP_COLUMN

Destructive

Field removed from a table

ALTER_COLUMN

Additive

Field type or default changed

ADD_FK

Additive

Foreign key constraint added

DROP_FK

Destructive

Foreign key constraint removed

ADD_TABULAR_TABLE

Additive

Child table for TABLE attribute

DROP_TABULAR_TABLE

Destructive

Child table removed

ADD_TABULAR_COLUMN

Additive

Column in child table added

DROP_TABULAR_COLUMN

Destructive

Column in child table removed

ALTER_TABULAR_COLUMN

Additive

Column in child table changed

For system apps, destructive changes are applied automatically (confirmedDestructive: true) because the manifest is the trusted source.

Migration Storage

Each system app schema has its own _app_migrations table. The table is created by SchemaGenerator.ensureSystemTables() during the first bootstrap.

A migration record contains:

Subsequent upgrade migrations contain a non-null snapshotBefore and a concrete list of changes.

Synthetic Entity Generation

The manifest targetBusinessTables are converted to EntityDefinition[] by buildSystemAppBusinessEntities() in the schema compiler. This function:

  1. Generates deterministic UUIDs for entities and fields using SHA-256 hashes of namespace:definitionKey:stage:kind:codename:tableName.

  2. Maps business table kinds (catalog, document, relation, settings) to runtime entity kinds.

  3. Creates synthetic presentation objects for localized display names.

  4. Resolves inter-table FK references via targetTableCodename.

The resulting EntityDefinition[] is identical in structure to what the metahub publication pipeline produces, allowing the same SchemaGenerator and SchemaMigrator to work with both paths.

Ensure vs. Apply

  • ensureRegisteredSystemAppSchemaGenerationPlans() is the startup entry point. It checks the current database state and decides whether to apply a full baseline, upgrade incrementally, or skip entirely.

  • applyRegisteredSystemAppSchemaGenerationPlans() always runs a fresh full generation. It is used by the CLI system-app-schema-apply command and during development reset scenarios.

Relationship to Metahub-Published Applications

System apps and metahub-published applications share the same DDL tools:

Metahub-published applications follow the same flow but receive their EntityDefinition[] from publication snapshots instead of TypeScript manifests.

Last updated