依 autoflow-agent workspace v2 設計把 PRD / 設計 / 架構 / 交付類 共享文件從個人層 .autoflow/(ignored)搬到 docs/autoflow/(進 git), 讓團隊可共享產品與架構文件,個人層只留 progress / review / testing 等 per-branch 筆記。 - 02-prd/ 21 個檔(PRD、features、market-analysis 等) - 03-design/ 18 個檔(design-spec、wireframes、flows 等) - 04-architecture/ 31 個檔(TDD、design-doc、ADR×14、API 規格等) - 07-delivery/ 3 個檔(project-summary、phase-0.6-handover、stage-deployment-setup) 合計 73 檔。原檔已從 .autoflow/ 移除(migration 工具執行 git mv, 但因 .autoflow/ 在 .gitignore 中、git 將此操作視為新增、無 rename history)。
5.5 KiB
5.5 KiB
ADR-001:visionA-backend 採用單一 Go 專案 / 雙 binary 結構
狀態
Accepted — 2026-04-21
背景 (Context)
visionA Cloud 後端需同時提供兩種完全不同性質的服務:
-
API Server(對 visionA-frontend)
- 面向瀏覽器,提供 REST + WebSocket 介面
- 無狀態:水平擴展容易,可放在無狀態的容器 / Serverless 執行環境
- 流量特性:一般 request/response,延遲敏感(< 200ms)
- 負載模式:可預測,隨 DAU 成長線性增加
-
Remote Proxy(對使用者端的 local-tool 或 local agent)
- 面向 local agent,提供 WebSocket 長連線 + yamux 多工
- 有狀態:每個 local agent 開一條 tunnel,session 綁在該 proxy 節點
- 流量特性:長連線(小時~天)、資料量差異大(控制指令 vs MJPEG / 推論串流)
- 負載模式:長連線 cost 主要在記憶體和 file descriptor,不是 CPU
POC(edge-ai-platform)已經將 edge-ai-server 和 relay-server 分拆成兩個 binary,但兩者位於不同的 module / 不同的 cmd 路徑,共用程式碼是透過 import 來達成。
決策 (Decision)
visionA-backend 採用一個 Go module、兩個 binary的結構:
visionA-backend/
├── go.mod # module "visiona-backend"
├── cmd/
│ ├── api-server/main.go # 對前端的 REST + WebSocket API
│ └── remote-proxy/main.go # 對 local agent 的 tunnel server
└── internal/ # 兩個 binary 共用
├── api/
├── session/
├── relay/
├── tunnel/
├── storage/
└── ...
- 兩個 binary 使用同一個 go.mod,可直接共用
internal/下的套件 - 各自有獨立的
cmd/xxx/main.go,選擇要載入哪些模組 - 共享狀態(session metadata)抽象為
internal/session的 interface:remote-proxy端用InMemoryStore(真正持有*yamux.Session)api-server端用ProxyClientStore(透過 internal HTTP 查remote-proxy,無本地 state)
- 雛形 Phase 0 就採雙 binary + internal HTTP(2026-04-22 Q1 裁決)—— 不做 all-in-one 單進程版本,因為那會讓
api-server有狀態、無法驗證真正的部署拓撲 - 不引入 Redis(POC 也沒用過;見 ADR-006)
考慮過的替代方案
| 方案 | 優點 | 缺點 | 排除原因 |
|---|---|---|---|
| 單 binary(API + Proxy 同一進程) | 最簡單,session 直接記憶體共用 | 無狀態 API 被迫與有狀態 Proxy 綁死;API 無法獨立水平擴展;擴容成本與 tunnel 連線耦合 | 無法水平擴展 API,Production 不可行 |
| 雙 module(兩個獨立 Go 專案) | 完全隔離 | 共用程式碼(types、protocol、session interface)要拆到第三個 module 或手動複製 | 維護兩倍 go.mod,共用改動成本高,POC 已走過這條路 |
| 三 binary(API + Proxy + Worker) | 關注點更分離 | 雛形階段沒有 worker workload,過度設計 | 雛形用不到 |
| 單 binary + feature flag 決定角色 | 部署單一 image,啟動時決定角色 | -mode=api vs -mode=proxy 會讓 main 變成 if/else 分派;binary 含不必要依賴 |
編譯時分開更乾淨 |
| 雙 module + 共用 shared module | 完全隔離 + 有共用 | 三個 Git repo / module 要同步版本,CI 複雜 | 雛形階段不需要 |
後果 (Consequences)
正面影響
- 部署獨立:API Server 可放在無狀態的容器集群(如 ECS Fargate / Cloud Run),Remote Proxy 放在支援長連線的 stateful 環境
- 擴展獨立:API 隨流量 auto-scale;Proxy 隨 tunnel 連線數 scale,兩者擴容邏輯不同
- Release 獨立:API 新功能不需要重啟 Proxy 進程(保持 tunnel 不中斷)
- 共用方便:types、protocol、session interface 直接放
internal/,編譯時檢查 - CI / Docker 簡單:兩個 Dockerfile,同一個 go.mod,依賴管理單一
負面影響(接受的取捨)
- 雛形就有 internal HTTP hop:
api-server → remote-proxy多一次 HTTP 呼叫。localhost ~0.1ms,跨機 LAN ~1-5ms。接受此成本換取「雛形部署拓撲 = Production 部署拓撲」 - 觀測稍複雜:兩個 binary 的 metrics / logs 要分別收集後 join
- 本機開發要開兩個進程:用
docker-compose或Makefile devtarget 同時啟動兩者
風險
- session 查詢延遲:雛形 localhost internal HTTP 呼叫(~0.1ms,可忽略);Phase 1 跨機 LAN ~1-5ms/次
- remote-proxy 是雛形有狀態單點:
remote-proxyprocess 重啟 → 所有 tunnel session 遺失,使用者需重新 pair(見 ADR-006) - 多節點路由複雜度(Phase 1):當有 N 個 remote-proxy 節點時,api-server 需要能找到「某個 token 的 tunnel 在哪個 proxy」——
tunnel.md§5.4 已列出候選方案,屆時新增 ADR - Phase 1 session metadata 共享機制未定:雛形不預先決定 Redis / Consul / Gossip,避免過早最佳化(見 ADR-006)
合規性
- 與 Architect 評估確認
- 與 PM 確認對業務的影響(部署形態)
- 使用者 Q1 裁決 C:雛形就用雙 binary + internal HTTP(2026-04-22)
- 成本影響已評估(Phase 1 多節點時 session metadata 共享方案定案後再估)
相關文件
- Design Doc §2(系統邊界與組件)
- Design Doc §4(水平擴展策略)
- TDD §1(專案骨架)、§2.3(session 模組)、§7(雛形啟動)
- ADR-006(雛形不引入 Redis)
api/api-internal.md(internal HTTP API 規格)