visionA/docs/autoflow/04-architecture/adr/adr-001-two-binaries.md
jim800121chen fb7da5d180 chore(autoflow): migrate .autoflow/ 共享層文件至 docs/autoflow/
依 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)。
2026-05-04 16:55:55 +08:00

5.5 KiB
Raw Permalink Blame History

ADR-001visionA-backend 採用單一 Go 專案 / 雙 binary 結構

狀態

Accepted — 2026-04-21

背景 (Context)

visionA Cloud 後端需同時提供兩種完全不同性質的服務:

  1. API Server(對 visionA-frontend

    • 面向瀏覽器,提供 REST + WebSocket 介面
    • 無狀態:水平擴展容易,可放在無狀態的容器 / Serverless 執行環境
    • 流量特性:一般 request/response延遲敏感< 200ms
    • 負載模式:可預測,隨 DAU 成長線性增加
  2. Remote Proxy(對使用者端的 local-tool 或 local agent

    • 面向 local agent提供 WebSocket 長連線 + yamux 多工
    • 有狀態:每個 local agent 開一條 tunnelsession 綁在該 proxy 節點
    • 流量特性:長連線(小時~天)、資料量差異大(控制指令 vs MJPEG / 推論串流)
    • 負載模式:長連線 cost 主要在記憶體和 file descriptor不是 CPU

POCedge-ai-platform已經將 edge-ai-serverrelay-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 HTTP2026-04-22 Q1 裁決)—— 不做 all-in-one 單進程版本,因為那會讓 api-server 有狀態、無法驗證真正的部署拓撲
  • 不引入 RedisPOC 也沒用過;見 ADR-006

考慮過的替代方案

方案 優點 缺點 排除原因
單 binaryAPI + Proxy 同一進程) 最簡單session 直接記憶體共用 無狀態 API 被迫與有狀態 Proxy 綁死API 無法獨立水平擴展;擴容成本與 tunnel 連線耦合 無法水平擴展 APIProduction 不可行
雙 module兩個獨立 Go 專案) 完全隔離 共用程式碼types、protocol、session interface要拆到第三個 module 或手動複製 維護兩倍 go.mod共用改動成本高POC 已走過這條路
三 binaryAPI + 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 RunRemote Proxy 放在支援長連線的 stateful 環境
  • 擴展獨立API 隨流量 auto-scaleProxy 隨 tunnel 連線數 scale兩者擴容邏輯不同
  • Release 獨立API 新功能不需要重啟 Proxy 進程(保持 tunnel 不中斷)
  • 共用方便types、protocol、session interface 直接放 internal/,編譯時檢查
  • CI / Docker 簡單:兩個 Dockerfile同一個 go.mod依賴管理單一

負面影響(接受的取捨)

  • 雛形就有 internal HTTP hopapi-server → remote-proxy 多一次 HTTP 呼叫。localhost ~0.1ms,跨機 LAN ~1-5ms。接受此成本換取「雛形部署拓撲 = Production 部署拓撲」
  • 觀測稍複雜:兩個 binary 的 metrics / logs 要分別收集後 join
  • 本機開發要開兩個進程:用 docker-composeMakefile dev target 同時啟動兩者

風險

  • session 查詢延遲:雛形 localhost internal HTTP 呼叫(~0.1ms可忽略Phase 1 跨機 LAN ~1-5ms/次
  • remote-proxy 是雛形有狀態單點remote-proxy process 重啟 → 所有 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 HTTP2026-04-22
  • 成本影響已評估Phase 1 多節點時 session metadata 共享方案定案後再估)

相關文件

  • Design Doc §2系統邊界與組件
  • Design Doc §4水平擴展策略
  • TDD §1專案骨架、§2.3session 模組、§7雛形啟動
  • ADR-006雛形不引入 Redis
  • api/api-internal.mdinternal HTTP API 規格)