# Schema migrations with oatpp-authkit oatpp-authkit (since v0.9.0) ships a declarative schema model: each decorator in a `Repository` stack exposes a static `DecoratorSchema kSchema` listing the columns/indexes/sidecar tables it needs. `SchemaBuilder<…>::create(table, exec)` composes the contributions into a single `CREATE TABLE` per entity table. `SchemaContract::verify` asserts the live DB matches at runtime. This document covers the **deploy-time** companion: how to evolve a live database between releases when the decorator stack changes. The recommended tool is [Atlas](https://atlasgo.io) (declarative schema-as- code, language-agnostic). ## The model: dev DB as desired state Atlas's "diff-driven migration" workflow is a clean fit: 1. **Desired state** — a schema produced by running `SchemaBuilder` once against an empty SQLite. The output of all `CREATE TABLE` / `CREATE INDEX` statements *is* the desired state. 2. **Current state** — what the production database actually contains. 3. **Migration** — `atlas migrate diff` compares (1) and (2) and emits versioned SQL files. 4. **Apply** — at deploy time, `atlas migrate apply` runs the new migration files against prod. Decorator code never runs ALTER at runtime. It only: - declares `kSchema` (compile-time); - runs `SchemaBuilder` against an empty DB (CI) — produces desired state; - runs `SchemaContract::verify` at app startup against the live DB — fails loud if a column/sidecar required by the stack is missing (i.e. the migration didn't run). ## Wiring it into a consumer's CI A consumer of oatpp-authkit (e.g. fewo-webapp, palibu, …) has its own DB schema that combines the authkit-shipped contributions with its own local tables. The schema-snapshot workflow: 1. **Build a small standalone tool** (`tools/schema_snapshot.cpp`) that instantiates the full set of `SchemaBuilder<…>::create` calls for every entity in the app, writing all DDL to a temporary SQLite. 2. **Atlas inspects** the resulting SQLite: ``` atlas schema inspect --url "sqlite://./tmp_schema.db" --format '{{ hcl . }}' > schema.hcl ``` 3. **Commit `schema.hcl`** to the repo. Diffs are reviewable per change. 4. **At deploy**: ``` atlas migrate diff --to "file://schema.hcl" --dir "file://migrations" \ --dev-url "sqlite://file?mode=memory" \ --format atlas atlas migrate apply --url "sqlite://prod.db" --dir "file://migrations" ``` The first `migrate diff` emits a versioned migration file; subsequent schema changes (decorator-level or app-level) regenerate the migration list. Each release includes the new migration files; deploy applies them. ## Atlas-free fallback For consumers that don't want Atlas as a dependency, `SchemaBuilder`'s output is plain SQL — pipe it into any migration tool (Flyway, goose, hand-rolled scripts). The C++ side stays unchanged. The runtime guarantee — `SchemaContract::verify` throwing on missing columns/sidecars — works regardless of which migration tool you used. ## Example: a consumer using oatpp-authkit's role_templates module The `dto::RoleTemplateDto` + `db::RoleTemplateSchema` + `repo::ConcreteRoleTemplateRepository` + `repo::TemporalRepository<…>` stack ships in oatpp-authkit since v0.10.0. A consumer wires it up like: ```cpp #include "oatpp-authkit/db/RoleTemplateDb.hpp" #include "oatpp-authkit/repo/ConcreteRoleTemplateRepository.hpp" // One-shot at CI: produce desired-state DDL. oatpp_authkit::repo::SchemaBuilder< oatpp_authkit::db::RoleTemplateSchema, oatpp_authkit::repo::TemporalRepository >::create("role_templates", exec); // Every app startup: assert the live DB matches. oatpp_authkit::repo::SchemaContract< oatpp_authkit::db::RoleTemplateSchema, oatpp_authkit::repo::TemporalRepository >::verify("role_templates", probe); // Routine repository use. auto rtdb = std::make_shared(executor); auto repo = oatpp_authkit::repo::makeRoleTemplateRepository(rtdb); auto liveTemplate = repo->findByEntityId("seed00000000000000000000role01rt"); ``` Atlas treats the `CREATE TABLE` output of `SchemaBuilder::create` as the desired state for those three tables (`role_templates` + `role_template_fields` + `user_role_assignments`); the consumer's own schema-snapshot tool aggregates these alongside its app-specific tables.