跳至主要内容

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 初始值:

tierspring.jpa.hibernate.ddl-autospring.flyway.enabled
dev/test(含 SIT/UAT)updatefalse
productionvalidatetrue

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 版本:

  1. 備份、clone 並盤點 schema/data;
  2. 驗證實際符合的 migration version;
  3. owner 明示執行 Flyway baseline 到該 version;
  4. 套用其後 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。