依 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)。
97 lines
5.5 KiB
Markdown
97 lines
5.5 KiB
Markdown
# ADR-001:visionA-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 開一條 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 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)
|
||
|
||
## 合規性
|
||
|
||
- [x] 與 Architect 評估確認
|
||
- [x] 與 PM 確認對業務的影響(部署形態)
|
||
- [x] 使用者 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 規格)
|