ADR-013: Database Schema 與資料變更管理
ADR 編號: 013 狀態: 已接受 (Accepted) 決策日期: 2026-09-01 最後修訂: 2026-09-03 決策者: AppFuse Team + AI Assistant 範圍: reference server 的 Entity Diff、Flyway artifacts 與環境設定
摘要
正式環境的 schema/data 變更以 Flyway immutable versioned SQL 管理;dev/test 可由人類選擇 Hibernate
ddl-auto:update。Entity 差異只在人類選定的兩個發行版本之間,以離線 Hibernate metadata snapshot
比較,不連接資料庫,也不因日常 refactoring 自動產生 migration。
Migration 被設計成獨立、人工觸發的能力。它不改寫既有需求、實作、測試、切版或部署方法論,亦不
成為 /release-app、/impl、/e2e、/deploy 等 skills 的 gate。/env-config 可以產出普通的 JPA/
Flyway 設定值,但不執行或驗證 migration。
production 的 reference 初始政策是 application-startup Flyway + Hibernate validate。既有非空 DB 必須
先由人類明示 Flyway baseline;禁止 baseline-on-migrate 自動猜測 adoption point。
背景
Entity 在產品開發期間通常不穩定。若每次 bug fix、rename 或 refactoring 都 materialize schema diff, 中間噪音會淹沒真正要交付的資料庫契約。發行版本則是團隊明確選定的邊界,適合比較「上一個已接受 版本」與「目前候選版本」。
同時,不同環境有不同目的:開發與 SIT/UAT 可能重視快速跟隨 Entity;production 重視可稽核、可重現且 順序明確的 SQL。框架不應從 hostname、profile 名稱或資料庫現況猜環境政策,應讓人類透過 Spring Boot 外部設定明示。
早期 ADR-013 實作曾把 migration disposition、Migration Plan、manifest、runner attestation、receipt、 release gate 與 deploy preflight 接入既有方法論。審閱後決定撤回這些耦合:它們把品質管制與部署策略 變成所有既有 skills 的強制責任,也違反「是否使用 migration 由人類決定」的目標。
決策
D-1:Entity Diff 比較發行版本,不比較環境 DB
- snapshot 位於 server 模組的
db/entity-model/mysql/entity-model.json。 - current model 由 Hibernate effective relational metadata 產生,涵蓋實際 entity bindings、inheritance、
naming strategy、converter 與
orm.xml。 - extractor 禁止 JDBC access;不得加入 datasource fallback。
- baseline 只在人類執行
bootstrap、accept或relabel時改變。 - target release 由操作者提供;不從
/release-app、git tag 或環境名稱自動推導。 draft是人工起稿,不等於可直接執行的完整 SQL。rename/drop/type/nullability、非 JPA objects 與資料 語意由人類裁決。
D-2:Flyway artifacts 由 application server 擁有
- MySQL migration 路徑為
src/main/resources/db/migration/mysql/V{N}__{snake_case}.sql。 V1__baseline.sql支援 fresh database;其後版本只能向前追加。- 已被任何環境套用的 versioned migration 不可修改或重用。
- Entity Diff 不負責 seed、reference data、backfill、stored objects 或非 JPA tables;這些由人類加入 SQL 或獨立操作程序。
D-3:環境行為由外部 Spring Boot 設定決定
reference 初始值:
| tier | spring.jpa.hibernate.ddl-auto | spring.flyway.enabled |
|---|---|---|
| dev/test(含 SIT/UAT) | update | false |
| production | validate | true |
classpath 的 Flyway 開關保持 false,只有外部 {appHome}/conf/{serviceName}.yml 明示後才啟用。
/env-config 可產出上述 key/value,但不判斷環境是否已準備好,也不執行 Entity Diff、baseline、migrate 或
validation。operator 可以依實際環境覆寫初始值。
production 採 application-startup Flyway 時,Spring Boot 的相依順序為:
DataSource → Flyway migrate → JPA EntityManagerFactory (`ddl-auto:validate`) → application ready
application runtime 因此需要 migration 所需權限。若組織禁止 runtime DDL,可由 operator 自行改採外部
runner 並把 startup Flyway 關閉;這是部署環境決策,不擴張 /deploy skill。
D-4:既有非空 DB 採明示 baseline
spring.flyway.baseline-on-migrate 固定為 false。若 schema 已由人工建立且符合某個 application 版本:
- 備份、clone 並盤點 schema/data;
- 驗證實際符合的 migration version;
- owner 明示執行 Flyway
baseline到該 version; - 套用其後 migration,再執行
validate、pending check、invariant 與 JPA validation。
baseline 只代表 adoption,不宣稱較早 scripts 曾被執行。不得固定假設 version 1,也不得手改 ledger。
D-5:既有 skills 維持 migration 導入前行為
Migration 不形成跨 skill orchestration:
/release-app只處理原有 CalVer、Feature Changelog、stamp 與 tag;不執行 Entity Diff、MySQL test、 manifest 或 migration acceptance。/deploy不產生或驗證 migration receipt,不執行 runner,也不因 pending migration 阻擋部署。/us、/sbe、/impl、/e2e-script、/e2e、/analyze、/impact、/prune-reference與 server Feature skills 不新增 persistence declaration、Migration Plan 或 database gate。/entity-diff是新增的獨立人工工具;/env-config只治理設定值。
若團隊日後需要 release、deployment 或品質 gate,應另作明確決策,不以 ADR-013 隱含擴張既有 skills。
D-6:驗證是可選的品質管制入口
migrationEvidenceTest 提供 MySQL Testcontainers 的 fresh replay、JPA validate 與 upgrade 情境驗證。它由
人類或品質管制程序明示執行;一般 check、/entity-diff、/release-app 與 /deploy 不會呼叫。
這保留可驗證能力,但不把「何時驗證」混入 migration 生成或環境操作機制。
操作流程
典型的人工作業如下:
團隊選定 source/target release
→ /entity-diff status 或 draft(離線)
→ 人工判讀並撰寫 Flyway SQL
→ 視需要人工執行 migrationEvidenceTest
→ /entity-diff accept target release
→ 由 operator 透過外部設定決定各環境採 JPA DDL 或 Flyway
→ production startup 執行 Flyway,再由 Hibernate validate
這些步驟沒有被 /release-app 或 /deploy 自動串接;操作者可依實際風險選擇順序與品質程序。
後果
正面
- 日常不穩定 Entity 不會產生大量無意義 migration。
- diff 可重現且不依賴任何環境資料庫狀態。
- production 具備 Flyway ledger 與 startup migration 的簡單運作模型。
- 團隊可在 dev/test 自由使用 JPA auto/update,不被 production 政策綁住。
- 既有方法論與 skills 不因導入 migration 增加額外表單、gate、Docker 或 DB 權限要求。
代價與風險
- 人類必須記得在適當版本邊界執行 diff、完成 SQL 並接受 snapshot。
- Entity Diff 無法發現 DB 被手動修改的 drift,也無法推導資料語意。
- application-startup migration 需要 runtime DDL 權限;多節點、長交易與鎖表操作需要額外協調。
- 沒有 release/deploy gate 時,錯誤的 migration 操作順序由團隊流程與品質管制承擔。
不採用的方案
每次 Entity 修改立即產生 migration
不採用。它會把開發過程中的暫態 rename/refactoring 固化成 SQL,使版本間真正差異難以辨識。
比較 Entity 與 live database
不採用。結果會依 SIT/UAT/production 當下 drift 改變,也迫使 generation tool 持有資料庫連線與權限。
由 release/deploy skills 自動 orchestration
不採用。這會把 migration 變成所有切版與部署的強制前置,並耦合 Docker、runner、attestation、receipt 與 DB 權限。是否需要這些 gate 屬品質或環境治理的另一項決策。
baseline-on-migrate=true
不採用。對非空 DB 自動建立 baseline 會把未知 schema 誤認為受支援版本;adoption 必須由 owner 明示。
驗收條件
- Entity Diff 的 extractor 無 JDBC access,且 snapshot 不進 production WAR。
/release-app、/deploy及其他既有 skills 無 Entity Diff、migration manifest、receipt 或 DB evidence 整合。- dev/test 與 production 的 JPA/Flyway 初始值可由外部設定產出並由人類修改。
- production 設為 Flyway on + JPA validate 時可在 fresh DB 完成啟動。
- 既有非空 DB 未 baseline 時 fail-loud;明示 baseline 且 schema 相符後可正常啟動並套用後續 migration。
migrationEvidenceTest可人工執行,但不屬於一般 build/release/deploy gate。