188 lines
6.2 KiB
Markdown
188 lines
6.2 KiB
Markdown
# ADR-024: API 分層架構 (Phase 16 絞殺者模式)
|
||
|
||
| 項目 | 內容 |
|
||
|------|------|
|
||
| **狀態** | ✅ 已採用 (Phase R-R2 完成,2026-04-01) |
|
||
| **日期** | 2026-03-26 |
|
||
| **決策者** | 首席架構師 + 統帥 |
|
||
| **Phase** | Phase 16 |
|
||
|
||
## 背景
|
||
|
||
Phase 16 架構大掃除需要明確的 API 分層規範,以支援絞殺者模式 (Strangler Fig Pattern) 的漸進式重構。
|
||
|
||
當前問題:
|
||
- Router 層存在業務邏輯 (32 項違規)
|
||
- 配置分散在多處
|
||
- 缺乏明確的層級邊界
|
||
|
||
## 決策
|
||
|
||
採用 **四層架構** 標準:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────┐
|
||
│ Router Layer (api/v1/*.py) │
|
||
│ - HTTP 轉發 ONLY │
|
||
│ - 參數驗證 (Pydantic) │
|
||
│ - 權限檢查 (Depends) │
|
||
│ - ❌ 禁止: Redis/DB/外部 API 直接呼叫 │
|
||
└─────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────┐
|
||
│ Service Layer (services/*.py) │
|
||
│ - 業務邏輯 │
|
||
│ - 外部 API 封裝 │
|
||
│ - 快取策略 │
|
||
│ - ✅ 可呼叫: Repository, 其他 Service │
|
||
└─────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────┐
|
||
│ Repository Layer (repositories/*.py) │
|
||
│ - 資料存取抽象 │
|
||
│ - SQL/ORM 操作 │
|
||
│ - Redis 快取 │
|
||
│ - ✅ 可呼叫: Model, Redis, PostgreSQL │
|
||
└─────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────┐
|
||
│ Model Layer (models/*.py) │
|
||
│ - Pydantic Schema │
|
||
│ - SQLAlchemy ORM │
|
||
│ - 純資料結構,無邏輯 │
|
||
└─────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## Router 層禁止清單
|
||
|
||
```python
|
||
# ❌ 禁止在 Router 層做的事
|
||
|
||
# 1. 直接 Redis 存取
|
||
from src.core.redis_client import get_redis # ❌
|
||
|
||
# 2. 直接 DB Session
|
||
from src.db.base import get_session # ❌
|
||
|
||
# 3. 直接外部 API 呼叫
|
||
async with httpx.AsyncClient() as client: # ❌
|
||
response = await client.get(external_url)
|
||
|
||
# 4. 內嵌 Lua 腳本
|
||
LUA_SCRIPT = """...""" # ❌
|
||
|
||
# 5. 複雜業務邏輯 (>10 行)
|
||
if condition1 and condition2: # ❌
|
||
# 複雜處理...
|
||
```
|
||
|
||
## Router 層允許清單
|
||
|
||
```python
|
||
# ✅ Router 層可以做的事
|
||
|
||
# 1. 參數驗證
|
||
@router.get("/items/{item_id}")
|
||
async def get_item(
|
||
item_id: str = Path(...),
|
||
limit: int = Query(default=10, ge=1, le=100),
|
||
) -> ItemResponse:
|
||
|
||
# 2. 權限檢查
|
||
async def get_item(
|
||
current_user: User = Depends(get_current_user),
|
||
):
|
||
|
||
# 3. 呼叫 Service
|
||
service = get_item_service()
|
||
result = await service.get_item(item_id)
|
||
|
||
# 4. 回傳轉換 (簡單)
|
||
return ItemResponse.from_orm(result)
|
||
```
|
||
|
||
## 絞殺者模式四階段
|
||
|
||
```
|
||
Phase 1: Identify (識別)
|
||
├── 標記現有違規代碼
|
||
├── 建立 Service 介面
|
||
└── 不改變行為
|
||
|
||
Phase 2: Deprecate (標記棄用)
|
||
├── 新代碼使用 Service
|
||
├── 舊代碼加 @deprecated
|
||
└── 監控舊路徑使用量
|
||
|
||
Phase 3: Migrate (遷移)
|
||
├── 逐步遷移到 Service
|
||
├── 每次遷移有測試覆蓋
|
||
└── 回滾計畫就緒
|
||
|
||
Phase 4: Remove (移除)
|
||
├── 確認無流量
|
||
├── 移除舊代碼
|
||
└── 更新文檔
|
||
```
|
||
|
||
## 與 leWOOOgo 積木化的關係
|
||
|
||
```
|
||
leWOOOgo 六大積木 API 四層對應
|
||
─────────────────────────────────────
|
||
BRAIN (決策) → Service Layer
|
||
ACTION (執行) → Service + Repository
|
||
SENSE (感知) → Repository Layer
|
||
MEMORY (記憶) → Repository Layer
|
||
OUTPUT (輸出) → Service Layer
|
||
SAFETY (安全) → Router (Depends) + Service
|
||
```
|
||
|
||
## 回滾策略
|
||
|
||
```yaml
|
||
# 功能開關 (core/config.py)
|
||
USE_NEW_LAYER: bool = Field(
|
||
default=False,
|
||
description="True=新分層, False=舊版內嵌",
|
||
)
|
||
|
||
# 回滾指令
|
||
kubectl set env deployment/awoooi-api USE_NEW_LAYER=false
|
||
```
|
||
|
||
## 後果
|
||
|
||
### 正面
|
||
- 清晰的層級邊界
|
||
- 可測試性提升 (每層獨立測試)
|
||
- 漸進式遷移,低風險
|
||
|
||
### 負面
|
||
- 短期開發成本增加
|
||
- 需要團隊學習新規範
|
||
|
||
## 執行進度
|
||
|
||
| Phase | 任務 | 狀態 | Commit |
|
||
|-------|------|------|--------|
|
||
| R1 | 絞殺者包裝 (USE_NEW_ENGINE) | ✅ | Phase 16 R1 |
|
||
| R2 | 移除內嵌重複邏輯 | ✅ | `c7b3f8f` (2026-04-01) |
|
||
| R3 | 抽取 Repository 層 | ✅ | Phase 22 (repositories/) |
|
||
| R4 | 瘦身 Router 層 | ✅ | `4118808` (2026-04-01) |
|
||
|
||
> Phase R-R2.1 首席架構師正式審查:72/100 條件通過。
|
||
> P0+P1 全部修復。P2 技術債清單見 ADR-046。
|
||
> 回滾方式已更新:git revert c7b3f8f d17b67c + kubectl rollout restart (USE_NEW_ENGINE 旗標已失效)。
|
||
|
||
## 相關文件
|
||
|
||
- ADR-005: BFF Architecture
|
||
- ADR-003: leWOOOgo Module Architecture
|
||
- ADR-046: 跨套件 Incident 型別統一 (BrainIncident vs local Incident)
|
||
- `feedback_strangler_fig_pattern.md`
|
||
- `reference_phase16_architecture.md`
|