visionA/docs/autoflow/04-architecture/db-integration-plan.md
jim800121chen 46958200eb docs(architecture): DB 接入規劃補 MySQL 版本估算 + 文件修訂
- 新增 §3.5 MySQL 版本估算(並列補充、不改 PG 版數字):
  MySQL 三範圍 9.2–15.7 / 18.3–31.4 / 24.6–42.5 人天,比 PG 約 +13%(中位)
  delta 主來源:model array 欄位→JSON 序列化、driver/migration/testcontainers 換、
  UUID + partial index 替代、交易隔離級別差異驗證;Redis delta=0
  含「該不該換」中立建議(由維運能力與組織標準主導)
- 移除 130 上其他專案 DB container 的具體名稱(保留「不該共用、用獨立 DB」結論)
- 測試占比敘述改為「接 DB 的客觀測試需要」(integration/邊界/回歸),數字未動

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 20:15:01 +08:00

36 KiB
Raw Blame History

visionA Backend 接資料庫 — 工時規劃文件Man-hours / Man-day 估算)

項目 內容
文件目的 提供 visionA-backend 各 in-memory store 接資料庫持久化的工時估算供主管對齊資源man-day、PM 排程、工程師排工使用。
適用範圍 visionA-backend 6 個現有 in-memory store 的持久化規劃。不含 cluster / converter_job 兩張未 wire 的表、不含 prod 環境另建 DB見 §8 範圍外)。
狀態 規劃,未實作。本文件為工時估算與技術規劃,非實作紀錄,無任何 production code 變更。
產出者 Architect Agent
最後更新 2026-06-16
數字基準 子任務級 man-hours1 人天 = 6.5 有效 hrs見 §2 估算假設)。

Executive Summary給主管

一句話結論:把 visionA-backend 接 DB 的工程已被拆到子任務級並完成保守工時估算,整體區間 2237.7 人天(視範圍);其中約 80% 的 Go 端工作不需要等 DB 開好就能開工,等 DB 連線資訊只卡最後 1.54 天的 stage 收尾驗證。

三種範圍的 man-day 區間

範圍 包含內容 Man-day 累計 Man-hours 適用情境
最小可行(只 model DB 基礎建設 + 模型庫接 Postgres + 精簡驗證 7.8 13.5 51 88 使用者最關心:模型庫能持久化、重啟資料還在
持久資料 上述 + device + pairing/session token 16 27.4 104 178 所有業務持久資料上 Postgressession 暫留 in-memory
完整 上述 + session 接 Redis + 交易/韌性 22 37.7 143 245 全部持久化 + Redis session + 交易/韌性

區間下限 = 一切順利;上限 = 含 schema 細節踩坑與測試補齊。三種範圍皆已含「DB 基礎建設」與「一次 stage 驗證」。

測試占比

接資料庫需要完整的測試保障,測試占整體工時 約 45%41%48%),高於一般功能開發的 2535%——因為接 DB 涉及 integration 測試testcontainers 真 DB、併發/連線失敗等邊界、以及既有 e2e 的回歸驗證。測試含三層unit / integration via testcontainers / 邊界),詳見 §5。

兩個關鍵結論

  1. DB 由他人開好、Go 端不受影響DB 機器與 visionA 專用 database 由他人在 stage host130開好並提供連線資訊。visionA 端不負責 provision DB,但 Go 端所有工作連線池、config、migration 撰寫、repository 實作、testcontainers 整合測試一小時都沒省——這些跟「DB 誰開」無關。整合測試改用 testcontainers本機/CI 一次性 DB不依賴 130更可靠。

  2. 「等 DB」只卡最後收尾:拿到 DB 連線資訊最小範圍可做到「testcontainers 全綠」的程度(約 6.511 個工作天的活),只差最後 stage 接上驗證1.52.5 天)。換句話說,只要範圍拍板,工程師可立刻開工,不會被「等 DB」block 大部分工作。


1. 背景與前提

1.1 為什麼這次接 DB 估得動

6 個 store 全部已有 Repository / Store interface + in-memory 實作,main.go 裡是乾淨的 6 個 NewInMemory* 呼叫點。接 DB = 加 Postgres/Redis 實作 + 換 1 行 wiringinterface 與所有 handler 不用動。這是估算偏可控的根本原因。

1.2 從零起手的部分(誠實交代)

go.mod 目前沒有任何 DB 依賴pgx / redis / golang-migrate 皆無),migrations/ 目錄不存在,config.go 無 DB 設定區塊。因此「DB 基礎建設」(塊 0是真的從零建連線池、config、migration 工具、CI Postgres。

此外pairing / session-token 兩個 store 目前用 plaintext 當 map key,接 Postgres 要改成 token_hash 當 PKdatabase.md §2.4 已是此意圖code 已有 HashToken())。這是邏輯改動、不是純 swap塊 3 須特別小心。

1.3 關鍵前提DB 由他人在 130 另開visionA 不負責 provision

此前提很重要避免讀者誤解「DB 現成可省一大塊」。

  • stage host 192.168.0.130 是整個 stage 的 docker hostvisionA / MC / FAA / converter / minio 都在上面)。
  • 130 上已存在的 DB container 都屬於其他服務 / 專案visionA 一個都不該共用。
  • 130:5432 port 雖 OPEN 但不對應任何已知 container(來源不明),不採信為 visionA 可用
  • 已確認:會有人在 130 上幫 visionA 另外開好專用 DBPostgres + 視需要 Redis開好後提供連線資訊。

對工時的影響

項目 visionA 端是否要做 說明
Provision DB 機器 / 起 container 不用(他人做) 省約 612 hrs
建 visionA database / 角色權限 不用(他人做,含在「給連線資訊」裡) 省約 24 hrs
連線池 / config / migration 撰寫 照樣要做 跟 DB 誰開無關
Repository 實作 + 整合測試 照樣要做 改用 testcontainers不依賴 130

→ 一句話:省下的是「DB provisioning + database 建置 + 130 維運邊界」(約 1020 hrs / 1.53 人天Go 端全部照做。 整合測試改用 testcontainers本機/CI 一次性 DB這比連 130 測試更可靠CI 能跑、隔離乾淨),但 testcontainers 設置成本(約 917 hrs如實算進塊 0。淨效果最小範圍總工時主要被「接 DB 必要的完整測試」佔比推高不是被「DB 誰開」推高。


2. 估算單位與假設(讓數字可信)

項目 設定 理由
基本單位 man-hours人時子任務加總 → 塊小計 → 範圍累計 細到子任務才能排工
Man-day 換算 1 人天 = 6.5 有效 hrs(非 8 扣掉 context switch、PR 溝通、等 CI、開會、被打斷的真實 overhead
人力假設 1 位熟 Go、熟本 codebase 的 backend 工程師 interface 已在、有現成 in-memory 測試可對照
hours 已含 實作 + 三層測試 + 一輪 code review 修正 每塊獨立列「self-review + 過 Reviewer」子任務
hours 不含 跨團隊等待(拿連線資訊、他人開 DB、需求變更、prod 另建 DB、CI 大改造意外 這些不可控、不放進工程估算
區間語意 下限 = 一切順利;上限 = 含 schema 細節array、unique×soft-delete、hash key 切換)踩坑與測試補齊
測試占比較高 約 45%(一般功能 2535% 接 DB 需 integration + 邊界 + 回歸測試,屬必要工程成本,詳見 §5

塊 0 第一次做最貴連線池、migration 機制、testcontainers、CI Postgres 都從零。完成後塊 13 每塊攤平、可偏區間低值。


3. DB 選型分工

對齊 database.md §2.7「Session 不落 DB、Phase 1 考慮 Redis」的既有立場。

3.1 放 PostgreSQL持久業務資料

Store package 理由
modelRepo internal/model 模型庫要長期保存、要查詢List by owner/chip/source、跨重啟不能掉。使用者最關心。
deviceRepo internal/device 裝置綁定身分長期保存、有 owner/serial unique 約束、有關聯查詢需求。
pairingStore internal/auth 配對 token 是「可撤銷的長期憑證」要稽核used_at/revoked_at重啟不能掉。
sessionTokenStore internal/auth session token 90 天長效、可撤銷、要查 parent token 稽核鏈。

3.2 放 Redis揮發 session有 TTL、高頻讀寫

Store package 理由
userSessionStore internal/usersession 瀏覽器 cookie session。高頻讀、有 idle/absolute TTL、掉了重登即可。Redis TTL 原生支援。
remote-proxy session internal/session tunnel session。特例handle 是 process-local 活連線物件yamux/WS不能序列化進 Redis。 單節點維持 in-memory多節點才需 Redis 存 Summaryhandle 仍留本地)。

3.3 為什麼不全放 Postgres / 不全放 Redis

  • 不全放 Postgrescookie session 高頻讀寫 + 自然過期Postgres 會變熱點、還要自己寫 cleanup jobRedis TTL 天生適配。
  • 不全放 Redismodel/device/token 要關聯查詢、unique 約束、稽核、長期保存Redis 做這些彆扭。
  • 結論:持久業務資料 → Postgres揮發 session → Redisdatabase.md 既有立場一致。

3.5 MySQL 版本估算(並列補充,不取代 PG 版)

本節為「若改用 MySQL 取代 PostgreSQL 作為持久業務資料庫」的並列工時估算,供主管在 DB 選型上對齊。§4 各塊與 §5 三範圍的數字仍以 PostgreSQL 為基準、不受本節影響;本節只估「換 MySQL 相對 PG 額外多出的工時delta並據此推出 MySQL 版的三範圍累計。Redis 部分(塊 4與 DB 選型無關delta = 0。

3.5.1 為什麼會有 delta現有 schema 用了 5 項 PG 專屬特性

現有 database.md §4 的 6 張表 schema 是針對 PostgreSQL 設計的,以下 5 項是 PG 原生、MySQL 沒有或需另解的特性,換 MySQL 每一項都要改 schema + repository code + 測試:

# PG 特性 用在哪 MySQL 對應做法 影響面
1 gen_random_uuid() UUID 主鍵 6 張表全用users/devices/models/pairing_tokens/clusters/converter_jobs BINARY(16) + app 端產 UUID推薦省空間CHAR(36) + UUID() migration + repository 掃描/綁定欄位邏輯
2 CITEXT(大小寫不敏感唯一) users.email VARCHAR + collationutf8mb4_0900_ai_ci migrationusers 為 Phase 1 stub影響小
3 TEXT[] array 欄位 users.roles、models.classes、models.input_shapeINT[] MySQL 無原生 arrayJSON 欄位MySQL 8+ repository 序列化/反序列化 delta 最大來源,集中在塊 1
4 JSONB clusters.devices_json、params MySQL JSON(無 JSONB binary 索引優化,功能足夠) 本期 cluster 未 wire§8 範圍外),實際影響小
5 partial index WHERE deleted_at IS NULL devices/models 軟刪唯一索引 MySQL 8 不支援 partial index → functional index 或全欄索引(略低效)或調整查詢 塊 1、塊 2 的 unique×soft-delete 語意

補充:本期實際 wire 的 6 個 store 中array 特性集中在 model塊 1的 classes + input_shape 與 users.rolesusers 為 Phase 1 stubJSONB 在 cluster範圍外。因此 delta 重心落在塊 1其次塊 2/3 的 UUID 與 partial unique。

3.5.2 各功能塊 PG vs MySQL 工時對照

下表以 §4/§5 各塊的 PG 區間為基準,列出 MySQL 版的額外 deltahrs順利卡關與成因。delta 已含「對應的 schema 改動 + repository code + 測試補齊」:

功能 PG hrs MySQL delta MySQL hrs delta 主因
0 DB 基礎建設 2036 +47 2443 Go driver 換pgx→go-sql-driver/mysql 或 GORM、migration 工具 MySQL dialect、testcontainers 換 MySQL image + helper 調整
1 model 接 DB 2236 +59 2745 classes/input_shape 兩個 array→JSON 欄位 + 序列化/反序列化 + round-trip 測試重打UUID 綁定partial index 替代functional index / 查詢調整)
2 device 接 DB 1932 +35 2237 UUID 綁定;UNIQUE(owner,serial)×soft-delete 的 partial unique 替代MySQL 8 無 partial index雙狀態欄位 round-trip
3 pairing + session token 3049 +24 3253 UUID 處理(BINARY(16)/CHAR(36)token_hash PK 在 MySQL 的型別/索引處理hash 切換邏輯本身不變)
4 session 接 Redis 1932 +0 1932 Redis 與 DB 選型無關
5 一致性/交易/韌性 2035 +24 2239 交易隔離級別預設不同PG 預設 Read Committed、MySQL InnoDB 預設 Repeatable Read → 一次性 token MarkUsed、cascade 撤銷等併發行為需重新驗證(含 gap lock 行為差異)
6 stage 部署 + e2e 回歸 1325 +12 1427 MySQL 版 migration 跑 + 版本特性確認JSON 型別、collation、functional index 支援,取代 PG 的 gen_random_uuid()/CITEXT 確認)
全部加總 143245 +1731 160276

delta 區間語意同主表:下限 = array/UUID/index 替代一次到位;上限 = JSON 序列化邊界、隔離級別行為差異、functional index 效能調校踩坑。測試估算框架沿用 §2/§5 既有原則(接 DB 的客觀整合/邊界/回歸測試需要delta 中已含對應測試補齊、未額外拉高占比。

3.5.3 MySQL 版三種範圍累計(與 PG 版並列)

沿用 §5.2 的三範圍定義(含塊組成、塊 6 計法一致),套上各塊 MySQL hrs

範圍 包含塊 PG 累計 hrs PG Man-day MySQL 累計 hrs MySQL Man-day
最小可行(只 model 0 + 1 + 6 精簡 5188 7.813.5 60102 9.215.7
持久資料 0 + 1 + 2 + 3 + 6 完整 104178 1627.4 119204 18.331.4
完整 0+1+2+3+4+5+6 143245 2237.7 160276 24.642.5

換算同 §21 人天 = 6.5 有效 hrs。最小範圍塊 6 取精簡值6.16.4,含 MySQL +12 delta中間範圍不重複計塊 6。

3.5.4 結論MySQL 比 PG 多多少、值不值得

整體 deltaMySQL 版相對 PG 版多 +1731 hrs約 +2.64.8 人天),以各範圍中位推算約 +11%~13%

  • 最小範圍:+914 hrs約 +18%,因基數小、塊 0+1 的 driver/array delta 占比被放大)
  • 持久資料範圍:+1526 hrs約 +14%
  • 完整範圍:+1731 hrs約 +12%

→ 比先前粗估的「+10~20%」更收斂:範圍越大、delta 百分比越低(基礎建設與 array 的一次性 delta 被攤平),落在 +11%~18% 之間,整體中位約 +13%

主要 delta 來源排序

  1. 塊 1model array→JSON — 最大,+59 hrsclasses + input_shape 兩個 array 欄位都要改 JSON 序列化。
  2. 塊 0driver/工具鏈換) — +47 hrs一次性。
  3. 塊 2UUID + partial unique 替代) — +35 hrs。
  4. 塊 5隔離級別行為驗證 — +24 hrs易被低估但攸關 token 一次性正確性。

中立建議(該不該換 MySQL

  • 若團隊已有 MySQL 維運能力、或公司技術標準就是 MySQL → 這 +13% 的開發 delta 多半值得,因為能省下 PG 的維運學習成本、監控/備份工具重建、DBA 熟悉度等長期維運成本(這部分不在本工時表內,但實務上往往大於一次性的 +2.64.8 人天)。
  • ⚠️ 若無特殊原因(團隊對兩者都不熟、或本來就用 PG → 對「這個 schema」而言 PostgreSQL 更自然arrayclasses/input_shape、JSONBcluster params、partial indexsoft-delete unique三項都是 PG 原生、零 delta換 MySQL 等於用額外工時把這些特性「降級模擬」回來。
  • 本文件立場(中立):純就「現有 schema 的契合度」與「一次性開發成本」看PG 略優;但 DB 選型應由維運能力與組織標準主導,本節提供的是「換 MySQL 的開發成本帳」,不是選型結論。

4. 各功能塊子任務拆解 + man-hours

標記:🟢 = 拿到 DB 連線資訊前就能做testcontainers 不依賴 130🔵 = 必須等 DB 連線資訊 / 真 DB 才能驗。

塊 0DB 基礎建設(所有 Postgres 塊的硬前置)

# 子任務 hrs順利卡關 依賴 標記
0.1 加依賴 pgx/v5pgxpool+ golang-migrate/v4go.mod/go.sum 整理 12 🟢
0.2 config.DatabaseConfigDSN/host/port/user/password/dbname/sslmode/pool size+ env 解析 + Enabled() 模式 + .env.example 23 0.1 🟢
0.3 連線池 internal/db/pool.gopgxpool 建池 + 啟動 ping + graceful shutdown 24 0.1,0.2 🟢/🔵
0.4 migration runnermigrations/ + 啟動自動 migrate up 或獨立 cmd/migrate+ 第一份骨架 migration 35 0.1 🟢
0.5 main.go wire 連線池(依 config 決定是否建池,本塊只接骨架) 12 0.3 🟢
0.6(測試) 整合測試基礎建設testcontainers-go 設置 + 測試 helpersetupTestDB(t) 自動 migrate + truncate+ fixture/factory builder 47 0.1,0.4 🟢
0.7(測試) CI 接 PostgresCI workflow 讓 testcontainers 在 CI 跑Docker-in-CI+ migration 跑通 + cache 調校 36 0.6 🟢
0.8(測試) 連線池/migration 本身測試ping、migrate up/down 冪等、池耗盡、連線失敗 fail-fast 24 0.6 🔵
0.9 整塊 self-review + 過 Reviewer + 修正 23 全部 🟢
塊 0 小計 2036

一次性最貴投資。「DB 由他人開好」讓 0.3/0.8 不用煩惱在 130 上裝/起 DB但 testcontainers0.6)成本如實算入。

塊 1model 接 Postgres ← 使用者最關心

# 子任務 hrs順利卡關 依賴 標記
1.1 PostgresModelRepositoryGet/List(by owner/chip/source)/Save(upsert ON CONFLICT)/Delete(soft) 46 塊0 🟢
1.2 input_shape INT[] / classes TEXT[] pgx array 映射 + Source enum + upsert 保留 CreatedAt 語意 24 1.1 🟢
1.3 migration create_models(含 faa_object_key TEXT nullable — database.md §4 漏此欄indexowner/chip/source、deleted_at IS NULL partial 23 塊0 🟢
1.4 main.go wiring 切換config 決定 Postgres/in-memory保留 in-memory 給 local dev+ seedDemoData 改寫進 DB 23 1.1,塊0 🟢
1.5(測試) unit/邏輯:對齊既有 inmemory_repository_test.goSaveAndGet、NotFound、List 三 filter、soft delete、Save 需 ID→ Postgres 版重打 35 1.1 🟢
1.6(測試) integration/真 DBarray round-trip、upsert 保留 CreatedAt、soft-delete 後 List 不含、List filter SQL 正確性、faa_object_key nullable round-trip 47 1.1,0.6 🟢
1.7(測試) 邊界:空 List、重複 Save、併發 Save 同 ID、連線中斷 Get 行為、context cancel 35 1.1,0.6 🟢
1.8 整塊 self-review + 過 Reviewer + 修正 23 全部 🟢
塊 1 小計 2236

model 欄位最多array + FAA 欄位 + 3 維 filter測試子任務1.51.7)合計 1017 hrs偏重。

塊 2device 接 Postgres

# 子任務 hrs順利卡關 依賴 標記
2.1 PostgresDeviceRepositoryGet/GetBySerial/List(by owner)/Save(upsert)/Delete(soft) 35 塊0 🟢
2.2 migration create_devices(雙狀態欄位 remote_status/last_seen_at/last_connected_at + UNIQUE(owner_user_id, serial_number) 23 塊0 🟢
2.3 UNIQUE(owner,serial) × soft-delete 語意(已刪 serial 能否重註冊 → partial unique index WHERE deleted_at IS NULL+ 決策註記 24 2.2 🟢
2.4 main.go wiring 切換 + seedDemoData device 12 2.1,塊0 🟢
2.5(測試) unit/邏輯:對齊既有 device testSaveAndGet、GetBySerial 跨 owner 不串、List by owner、soft delete、再刪回 NotFound、保留 CreatedAt 35 2.1 🟢
2.6(測試) integration/真 DBunique 衝突、partial unique 讓已刪 serial 可重註冊、雙狀態欄位 round-trip、upsert 保留 CreatedAt 46 2.1,0.6 🟢
2.7(測試) 邊界:空 List、併發註冊同 serial、連線中斷、context cancel 24 2.1,0.6 🟢
2.8 整塊 self-review + 過 Reviewer + 修正 23 全部 🟢
塊 2 小計 1932

塊 3pairing_token + session_token 接 Postgres

# 子任務 hrs順利卡關 依賴 標記
3.1 PostgresPairingStoreCreate/Validate/MarkUsed/Revoke/List/CleanupExpired 46 塊0 🟢
3.2 PostgresSessionTokenStoreCreate/Get/Revoke/CleanupExpired含 parent_token_hash 稽核鏈) 35 塊0 🟢
3.3 關鍵改動plaintext→token_hash 當 PK。Validate/Get 先 HashToken() 再查;確保所有呼叫端傳 plaintext 進、內部一致 hash 35 3.1,3.2 🟢
3.4 migration create_pairing_tokenspairing + session token 是否共表 by kind — 需決策,傾向共表;含 used_at/revoked_at/expires_at/parent_token_hash + index 24 塊0 🟢
3.5 main.go wiring 切換(兩個 store+ seedDemoData pairing token 23 3.1,3.2,塊0 🟢
3.6(測試) unit/邏輯pairing:對齊既有 testCreateAndValidate、unknown token、MarkUsed 一次性+冪等、Revoke、CleanupExpired、List by user、Validate expired 35 3.1 🟢
3.7(測試) unit/邏輯session token:對齊既有 testCreateAndGet、NotFound、expired、Revoke 冪等、Revoke NotFound、CleanupExpired、NeverExpires ttl=0 35 3.2 🟢
3.8(測試) integration/真 DBhash 當 PK 查詢正確性、TTL 過期、一次性 used 的 DB 層 race兩併發 MarkUsed 只一成功)、撤銷稽核欄位、共表 kind 隔離 58 3.1,3.2,0.6 🟢
3.9(測試) 邊界:併發 Validate 同 token、CleanupExpired 大量資料、連線中斷、context cancel 24 0.6 🟢
3.10 整塊 self-review + 過 Reviewer + 修正 34 全部 🟢
塊 3 小計 3049

Postgres 三塊裡最重:兩個 store + plaintext→hash 邏輯切換(漏一個呼叫端就驗不過)+ 一次性語意的 DB 層併發正確性測試。測試子任務3.63.9)合計 1322 hrs反映「token 語意錯會出安全問題」的保守估法。

加註(不在本塊預設範圍)remote-proxy 目前只驗 token 格式不查 store。若 Phase 1 要新增 GET /internal/session-token/:token 給 remote-proxy 拉驗證 → 另 +47 hrs,不含在塊 3 小計。

塊 4session 接 RedisuserSessiontunnel session 維持 in-memory

⚠️ 此塊需 Redis。若本期不做塊 4可叫他人先不用開 Redis,只開 Postgres。測試用 miniredis純 Go in-process或 testcontainers Redis不依賴 130。

# 子任務 hrs順利卡關 依賴 標記
4.1 加依賴 redis/go-redis/v9 + config.RedisConfighost/port/password/db index+ env + .env.example + Enabled() 23 🟢
4.2 internal/db/redis.go 連線 + 啟動 ping + graceful close 13 4.1 🟢/🔵
4.3 RedisUserSessionStoreCreate/Get/Update/Delete/CleanupExpired用 Redis TTLidle + absolute 雙 TTL取代手動 cleanup goroutineExtra map JSON 序列化 46 4.1,4.2 🟢
4.4 main.go wiring 切換 + 移除/停用 runUserSessionCleanup goroutine改靠 Redis TTL 12 4.3 🟢
4.5(測試) unit/邏輯:對齊既有 usersession testCreateAndGet、NotFound/EmptyID、Get 回副本、Update 移 LastSeenAt 不動 CreatedAt、Update NotFound/Nil、Delete 冪等、Extra round-trip、context cancel 46 4.3 🟢
4.6(測試) integration/真 RedisTTL 實際過期、idle vs absolute 雙 timeout、序列化 round-trip、key 命名/隔離 35 4.3 🟢
4.7(測試) 邊界 + 併發race detector、Redis 連線中斷 store 行為、TTL 邊界 24 4.3 🟢
4.8 整塊 self-review + 過 Reviewer + 修正 23 全部 🟢
塊 4 小計 1932

tunnel sessioninternal/sessionvalue 是活的 yamux Handle 不可序列化 → 維持 in-memory單節點本塊不動。跨節點「Summary 放 Redis、handle 留本地」列範圍外(要的話另估 +1326 hrs ≈ 24 人天)。

塊 5一致性 / 交易 / 連線韌性 / 健康檢查

# 子任務 hrs順利卡關 依賴 標記
5.1 internal/db/tx.go 交易 helperpgx tx 包裝 + rollback on error + context 24 塊0 🟢
5.2 跨 store 交易:建 Device + PairingToken 同 tx、刪 Device cascade 撤銷 token、converter job 完成→建 model transactional upsert 47 5.1,塊1-3 🟢
5.3 連線韌性pool retry / context timeout / 斷線重連 / fail-fast vs degrade 策略(策略需使用者裁決) 36 塊0 🟢
5.4 /healthz 擴充 DB/Redis ping + 統一 pgx error → internal/api/errors.go 映射 24 塊0 🟢
5.5(測試) 交易測試rollback中途失敗整筆回滾、cascade 撤銷 token 交易邊界、併發交易 46 5.2,0.6 🟢
5.6(測試) 韌性測試連線中斷模擬、timeout、健康檢查回應DB down 時 /healthz 行為) 35 5.3,5.4,0.6 🟢
5.7 整塊 self-review + 過 Reviewer + 修正 23 全部 🟢
塊 5 小計 2035

區間寬反映「做到多嚴謹」的彈性:最小只做 /healthz + 基本 tx取下限完整韌性策略retry/degrade/cascade 全做)取上限。

塊 6stage 部署接 130 + e2e 回歸驗證

# 子任務 hrs順利卡關 依賴 標記
6.1 拿到他人開好的 DB 連線資訊後:.env.stage 注入 DSN/憑證(走既有 secrets 機制,不進 repo 12 DB 連線資訊 🔵
6.2 在 130 的 visionA database 跑 migration + 確認 PG 版本支援 gen_random_uuid()/CITEXT不支援則調 migration 24 6.1 🔵
6.3 stage 部署設定 / compose / CI 部署步驟接上 DB env 23 6.1 🔵
6.4(測試) e2e 持久化驗證:登入(OIDC)→配對→上傳 model→列 model→重啟 backend→資料還在(核心驗收)+ 空庫/seed 啟動正常 35 6.2,6.3 🔵
6.5(測試/回歸) 回歸測試:接 DB 後既有 e2e/integration約 6+ 檔)重跑,修因持久化行為改變而壞的測試 48 6.2 🔵/🟢
6.6 部署驗證彙報 + 修 stage 特有問題 13 全部 🔵
塊 6 小計 1325

6.5 回歸測試刻意估寬48 hrs既有 6+ 個 e2e/integration 測試檔,接 DB 後「重啟資料還在」「seed 行為」「token hash 切換」都可能讓 in-memory 假設失效,要逐一確認。這是接 DB 回歸驗證的必要成本。


5. 工時總表與測試占比分析

5.1 各塊 man-hours 總表

功能 hrs順利卡關 換算 Man-day÷6.5 DB 主要依賴
0 DB 基礎建設(連線池/config/migration/testcontainers/CI 2036 3.15.5 PG
1 model 接 Postgres含 FAAObjectKey 2236 3.45.5 PG 塊0
2 device 接 Postgres 1932 2.94.9 PG 塊0
3 pairing + session token 接 Postgres 3049 4.67.5 PG 塊0
4 session 接 RedisuserSession 1932 2.94.9 Redis 與塊0平行
5 一致性/交易/韌性/健康檢查 2035 3.15.4 PG 塊13
6 stage 部署接 130 + e2e 回歸驗證 1325 2.03.8 PG+Redis 想驗的塊
全部加總 143245 2237.7

5.2 三種範圍累計

範圍 包含塊 累計 hrs Man-day 說明
最小可行(只 model 0 + 1 + 6 精簡(只跑 6.16.4 約 916 hrs 5188 7.813.5 模型庫持久化 + 驗證重啟資料還在。塊 0 是硬前置。
持久資料 0 + 1 + 2 + 3 + 6 完整 104178 1627.4 業務持久資料上 Postgres。session 仍 in-memory內測可接受
完整 0+1+2+3+4+5+6 143245 2237.7 全部持久化 + Redis session + 交易/韌性。tunnel 多節點 summary 範圍外(另 +1326 hrs

三種範圍皆已含塊 0 與一次塊 6 驗證;中間範圍不重複計塊 6。最小範圍的塊 6 只跑 6.16.4(不含完整回歸 6.5),故取精簡值。

5.3 測試占比分析

測試子任務 測試 hrs 該塊總 hrs 測試占比(中位)
0 0.6+0.7+0.8 917 2036 ~46%
1 1.5+1.6+1.7 1017 2236 ~47%
2 2.5+2.6+2.7 915 1932 ~47%
3 3.6+3.7+3.8+3.9 1322 3049 ~44%
4 4.5+4.6+4.7 915 1932 ~47%
5 5.5+5.6 711 2035 ~33%
6 6.4+6.5 713 1325 ~53%
  • 純測試子任務加總64110 hrs。
  • 總工時143245 hrs。
  • 測試占比 ≈ 45%(中位),範圍 41%48%。

一般功能「測試占 2535%」是常態;本估約 45%,反映接 DB 所需的三層測試integration/邊界/回歸)+ testcontainers 一次性基礎建設。testcontainers/CI 一次性基礎建設(塊 0 的 917 hrs是大頭——做一次、後續所有塊共用。若日後評估「測試估太保守」最先可砍各塊「邊界」子任務的卡關上限不建議砍 integration 與回歸(那是「重啟資料還在」的核心保證)。


6. 可先做(不卡 DBvs 等 DB 的劃分

6.1 拿到 DB 連線資訊前「就能先做」(🟢,約 80% 工作)

  • 塊 0加依賴、config、連線池建池邏輯、migration runner+骨架、main.go 接骨架、testcontainers 設置、CI Postgres。
  • 塊 1/2/3全部 repository 實作 + migration 撰寫 + 全部測試unit + integration via testcontainers + 邊界)。testcontainers 自己起一次性 DB完全不依賴 130。
  • 塊 4全部miniredis / testcontainers Redis
  • 塊 5tx helper、韌性邏輯、health 擴充、交易測試testcontainers

拿到連線資訊前,最小範圍(塊 0+1可做到「testcontainers 全綠」(約 6.511 個工作天的活),只差最後 stage 接上驗證。

6.2 必須等 DB 連線資訊 / 真 DB🔵

  • 塊 00.3 連真 DB ping 確認、0.8 對真 DB 測試(可先用 testcontainers 跑、再對 stage DB 確認一次)。
  • 塊 6整塊(注入憑證、在 130 跑 migration、部署設定、e2e、stage 回歸、彙報)—— 這是唯一硬卡 DB 連線資訊的塊。

7. 排程建議(假設 1 個 backend 全職)

7.1 依賴關係(哪些可平行)

塊0基礎建設──┬──► 塊1model──┐
                ├──► 塊2device─┤
                ├──► 塊3token──┼──► 塊5交易/韌性需塊1-3
                                   │
塊4Redis─────與塊0平行獨立─┘

塊6stage 驗證)◄── 拿到 DB 連線資訊 + 想驗的塊完成
  • 塊 0 是所有 Postgres 塊的硬前置,必須先做。
  • 塊 4Redis可與塊 0 平行(依賴獨立)—— 單人全職時意義不大,列為「若有第二人可平行」。
  • 塊 1/2/3 在塊 0 完成後可任意順序,使用者最關心 model → 建議塊 1 優先。
  • 塊 5 需塊 13 的 store 都在才能做跨 store 交易。
  • 塊 6 卡 DB 連線資訊(唯一硬卡外部)。

7.2 最小範圍(塊 0+1單人全職時程

階段 子任務 工作天×6.5hr/day
第 1 階段(拿連線資訊前可做) 塊0 全部 🟢2036 hrs+ 塊1 全部 🟢2236 hrstestcontainers 全綠) 6.511 天
第 2 階段(拿到連線資訊後) 塊6 精簡6.16.4916 hrs+ 塊0 真 DB 確認 1.52.5 天
最小範圍合計 約 813.5 個工作天

第 1 階段6.511 天的活)完全不卡 DB——只要拍板做最小範圍backend 可立刻開工做到 testcontainers 全綠,等他人把 130 的 DB 開好、給連線資訊,再花 1.52.5 天接上 stage 驗證收尾。「等 DB」不會 block 大部分工作。

7.3 持久資料範圍單人全職時程

  • 第 1 階段(拿連線資訊前):塊 0+1+2+3 全部 🟢 ≈ 91153 hrs ≈ 1424 天
  • 第 2 階段(拿到後):塊 6 完整(含回歸 6.5)≈ 1325 hrs ≈ 24 天
  • 合計 約 1628 個工作天

8. 範圍外(本估算未計入)

要做再另加,不含在上述任何範圍:

項目 估算 說明
cluster / converter_job 兩張表 未估 目前 main.go 無對應 in-memory store 被 wireconverter 是 stub、cluster 只有 types
remote-proxy /internal/session-token 驗證 endpoint +47 hrs 目前 remote-proxy 只驗 token 格式不查 store
tunnel session 多節點 Redis summary +1326 hrs24 人天) handle 不可序列化需「Summary 放 Redis、handle 留本地」混合實作
prod 環境另建 DB 未估 prod 是否用 130 或另一套RDS/Cloud SQL/自建)未定,影響韌性/HA/成本

9. 待提供 / 待決策清單

9.1 待他人提供的 DB 連線資訊(開好後請給)

項目 為什麼需要 阻塞
Postgreshost / port / user / password / dbname 塊 6 連線config 可先用 placeholder 寫好) 塊 6
PG 版本 影響 gen_random_uuid()(需 PG13+ 或 pgcrypto、CITEXT extension → 影響 migration 寫法 塊 6.2
sslmodedisable/require/verify-full DSN 組裝 塊 6.1
visionA database 是否已建好、app role 權限 確認 migration 能跑、有無建 extension 權限 塊 6.2
Redis有無開、host/port、有無密碼 決定塊 4 是否本期做;若不做可叫他人先不開 Redis 塊 4、塊 6

9.2 待使用者決策(影響估算/設計)

# 項目 影響
1 範圍:最小 / 持久資料 / 完整session→Redis 本期做嗎? 決定總工時與要不要請他人開 Redis
2 session token 與 pairing token 是否共表by kind 塊 3.4 schema
3 DB 掛掉降級策略fail-fast(503) 還是 degrade 塊 5.3 韌性
4 已 soft-delete 的 device serial 能否重註冊? 塊 2.3 partial unique index
5 remote-proxy 是否本期要 /internal/session-token 驗證 endpoint 塊 3 加註(+47 hrs

10. 給 Orchestrator 的後續建議database.md 更新,本文件未動)

以下為 database.md(共享文件)應同步更新的缺口,建議另派 Architect 處理,不在本工時估算內。

  1. §2.3/§4 models table 補 faa_object_key TEXTnullable—— code 已有ADR-017schema 漏。
  2. §4 補 DatabaseConfig / RedisConfig env 規格DSN/pool/sslmode/redis password
  3. §5 補 migration 第一份清單(哪些 table 在第一個 migration
  4. 可選§2.7 補 tunnel session「handle 不可序列化、單節點維持 in-memory」結論。
  5. database.md 提及「DB 在 130 現成可用」字眼,需更正為「DB 由他人在 130 另開 visionA 專用實例並提供連線資訊visionA 端不負責 provision只負責跑 migration 與接上」(與 §1.3 前提一致)。