# Skill 09: Phase 16 Strangler Pattern Expert

> 版本: v1.1
> 建立: 2026-03-26 (台北時區)
> 更新: 2026-04-01 (台北時區) — Phase R 完成，USE_NEW_ENGINE 廢棄
> 管轄: 絞殺者模式重構、API 分層架構、漸進式遷移

---

## 觸發條件

修改以下檔案時自動載入:
- `apps/api/src/api/v1/*.py` (Router 層)
- `apps/api/src/services/*.py` (Service 層)
- `apps/api/src/repositories/*.py` (Repository 層)
- `apps/api/src/_archived/**` (封存區)
- 任何涉及 **核心邏輯重構** 的任務

---

## 核心原則

> **Why:** Phase 16 驗證了大型重構必須採用漸進式替換，而非一次性重寫。

**三大保證:**
1. **隨時可回滾** - 環境變數開關切換
2. **功能不中斷** - 新舊邏輯並存
3. **可驗證每個階段** - 48 小時觀察期

---

## 絞殺者模式四階段

### Phase 1: Identify (識別)

```
目標: 標記現有違規代碼，建立 Service 介面，不改變行為
```

```python
# 1. 標記需要遷移的代碼
# @deprecated: Phase 16 - 待遷移至 XxxService (2026-XX-XX)
def legacy_function():
    ...

# 2. 建立新 Service 介面 (不實作)
class IXxxService(Protocol):
    async def process(self, data: XxxInput) -> XxxOutput: ...
```

**產出物:**
- [ ] 違規代碼清單
- [ ] Service Interface 定義
- [ ] 遷移計畫文檔

---

### Phase 2: Deprecate (標記棄用)

```
目標: 新代碼使用 Service，舊代碼加 @deprecated，監控舊路徑使用量
```

```python
# 1. 建立環境變數開關
USE_NEW_ENGINE = os.getenv("USE_NEW_ENGINE", "false").lower() == "true"

# 2. 新舊邏輯並存 (Strangler Wrapper)
if USE_NEW_ENGINE:
    result = await new_service.process(data)
else:
    result = await legacy_engine.process(data)
```

> ⚠️ **歷史注意事項 (2026-04)**
> `USE_NEW_ENGINE` 已於 Phase R 完成歷史使命，永久設為 True 後於 Phase R S-02 移除。
> **未來不再需要此類 feature flag**。Phase R 後新引擎已成為唯一執行路徑。
> 以下 Phase 3/4 中關於此 flag 的指令僅供歷史參考。

**驗證期:** 48 小時觀察，無異常後才進入 Phase 3

**監控指標:**
- 舊路徑呼叫次數 (SignOz Counter)
- 錯誤率對比
- 延遲 P99 對比

---

### Phase 3: Migrate (遷移)

```
目標: 逐步遷移到 Service，每次遷移有測試覆蓋，回滾計畫就緒
```

```python
# 1. 預設啟用新邏輯 (Phase R 後已移除此 flag，新邏輯即為唯一路徑)
# USE_NEW_ENGINE = os.getenv("USE_NEW_ENGINE", "true").lower() == "true"  # ← 已廢棄 2026-04

# 2. 保留舊邏輯作為回滾
# 3. 封存死代碼到 _archived/
```

**封存指令:**
```bash
# 禁止直接刪除！必須移動到 _archived/
git mv src/old_module.py src/_archived/old_module.py

# 建立 README 說明回滾方式
echo "# Archived Code - Phase 16 R2" > src/_archived/README.md
```

**回滾指令:**
```bash
git mv src/_archived/old_module.py src/old_module.py
# 注意: USE_NEW_ENGINE 已於 Phase R 2026-04 移除，不再需要 kubectl set env
```

---

### Phase 4: Remove (移除)

```
目標: 確認無流量，移除舊代碼，更新文檔
```

**移除條件:**
- [ ] 30 天觀察期無舊路徑呼叫
- [ ] 所有測試通過
- [ ] 統帥確認可刪除

**移除後:**
- 更新 ADR 文檔
- 移除環境變數開關
- 更新 MEMORY.md

---

## Router 層禁止清單 (ADR-024)

```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)
```

---

## Service 層職責

```
Service Layer 負責:
┌─────────────────────────────────────────────────┐
│ 1. 業務邏輯編排                                  │
│ 2. 呼叫 Repository 進行資料存取                  │
│ 3. 呼叫其他 Service 組合功能                     │
│ 4. 外部 API 封裝 (httpx)                        │
│ 5. 快取策略                                      │
│ 6. 錯誤處理與重試                                │
└─────────────────────────────────────────────────┘
```

**Service 實作範例:**
```python
# services/approval_execution.py
class ApprovalExecutionService:
    def __init__(
        self,
        approval_repo: IApprovalRepository,
        incident_repo: IIncidentRepository,
        executor: IExecutor,
    ):
        self._approval_repo = approval_repo
        self._incident_repo = incident_repo
        self._executor = executor

    async def execute_approval(
        self, approval_id: str
    ) -> ExecutionResult:
        """執行已核准的操作"""
        approval = await self._approval_repo.get(approval_id)
        if not approval:
            raise ApprovalNotFoundError(approval_id)

        result = await self._executor.execute(approval.action)
        await self._approval_repo.update_status(
            approval_id, "executed"
        )
        return result
```

---

## Repository 層職責

```
Repository Layer 負責:
┌─────────────────────────────────────────────────┐
│ 1. 資料存取抽象 (PostgreSQL/Redis)               │
│ 2. SQL/ORM 操作                                  │
│ 3. Redis 快取操作                                │
│ 4. 資料轉換 (ORM → Pydantic)                    │
│ 5. 查詢最佳化                                    │
└─────────────────────────────────────────────────┘
```

**Repository 實作範例:**
```python
# repositories/incident_repository.py
class IncidentDBRepository(IIncidentRepository):
    async def get(self, incident_id: str) -> Incident | None:
        async with get_db_context() as session:
            stmt = select(IncidentORM).where(
                IncidentORM.id == incident_id
            )
            result = await session.execute(stmt)
            orm = result.scalar_one_or_none()
            return Incident.model_validate(orm) if orm else None

    async def save(self, incident: Incident) -> None:
        async with get_db_context() as session:
            orm = IncidentORM(**incident.model_dump())
            session.add(orm)
            await session.commit()
```

---

## 四層架構圖

```
┌─────────────────────────────────────────────────┐
│  Router Layer (api/v1/*.py)                     │
│  - HTTP 請求處理、參數驗證、回應格式化          │
│  - ❌ 禁止: Redis/DB/外部 API 直接呼叫         │
├─────────────────────────────────────────────────┤
│  Service Layer (services/*.py)                  │
│  - 業務邏輯、流程編排                           │
│  - ✅ 可呼叫: Repository, 其他 Service          │
├─────────────────────────────────────────────────┤
│  Repository Layer (repositories/*.py)           │
│  - 資料存取抽象                                 │
│  - ✅ 可呼叫: Model, Redis, PostgreSQL          │
├─────────────────────────────────────────────────┤
│  Model Layer (models/*.py)                      │
│  - Pydantic Schema, SQLAlchemy ORM              │
│  - 純資料結構，無邏輯                           │
└─────────────────────────────────────────────────┘
```

---

## 回滾策略

### 環境變數開關

> ⚠️ **Phase R 後 (2026-04) 更新**: `USE_NEW_ENGINE` / `USE_NEW_LAYER` 等 feature flag 已完成歷史使命並移除。
> 新架構無需此類開關，新引擎為唯一路徑。以下為歷史參考。

```yaml
# core/config.py (歷史版本)
# USE_NEW_LAYER: bool = Field(
#     default=False,
#     description="True=新分層, False=舊版內嵌",  # ← 已廢棄 Phase R 2026-04
# )
```

### 回滾指令

```bash
# Phase R 後回滾方式: 不再依賴環境變數，改用 git rollback
# 1. 恢復封存檔案 (如有需要)
git mv src/_archived/old_module.py src/old_module.py
git commit -m "rollback: 恢復 old_module.py"

# 2. 重新部署
kubectl rollout restart deployment/awoooi-api
```

### 回滾驗證

```bash
# 確認版本
kubectl exec -it deploy/awoooi-api -- cat /app/VERSION

# 確認 Health
curl -sf https://api.awoooi.wooo.work/api/v1/health | jq
```

---

## 與 leWOOOgo 積木化的整合

```
leWOOOgo 六大積木        API 四層對應
─────────────────────────────────────
BRAIN (決策)      →    Service Layer
ACTION (執行)     →    Service + Repository
SENSE (感知)      →    Repository Layer
MEMORY (記憶)     →    Repository Layer
OUTPUT (輸出)     →    Service Layer
SAFETY (安全)     →    Router (Depends) + Service
```

**整合規則:**

```python
# ✅ 正確: Service 使用 packages/ 積木
from lewooogo_brain.engines import IncidentEngine
from lewooogo_data.providers import DualMemoryProvider

class IncidentService:
    def __init__(self):
        self._engine = IncidentEngine(DualMemoryProvider(...))

# ❌ 禁止: 重複實作已存在的積木功能
class IncidentEngine:  # 已存在於 lewooogo-brain
    ...
```

---

## 強制標註格式

每個修改必須包含:

```python
# Phase 16 R{N} (2026-XX-XX 台北時區)
# 動作: {新增/移除/修改}
# 原因: {簡述}
# 執行者: Claude Code
# 回滾: {回滾指令或說明}
```

**範例:**
```python
# Phase 16 R2 (2026-03-26 台北時區)
# 動作: 封存
# 原因: 業務邏輯已遷移至 ApprovalExecutionService
# 執行者: Claude Code
# 回滾: git mv src/_archived/approval.py src/services/approval.py
```

---

## 驗證清單

修改前檢查:
- [ ] 環境變數開關已設定
- [ ] 舊邏輯仍可運作
- [ ] 測試覆蓋新舊邏輯

遷移後檢查:
- [ ] 48 小時無錯誤
- [ ] 封存檔案有 README
- [ ] 分層架構符合 ADR-024
- [ ] 所有測試通過

移除前檢查:
- [ ] 30 天觀察期已過
- [ ] SignOz 確認無舊路徑流量
- [ ] 統帥已確認

---

## 封存目錄結構

```
src/_archived/
├── README.md           # 回滾說明 + 刪除時間表
├── routes/
│   └── approvals.py    # 舊 Router (Phase 16 R2)
├── services/
│   └── approval.py     # 舊 Service (Phase 16 R2)
└── .do_not_import      # 防止意外 import 的空檔
```

**README.md 範本:**
```markdown
# Archived Code - Phase 16

## 封存日期
2026-03-26 (台北時區)

## 原因
業務邏輯已遷移至新分層架構

## 回滾方式
1. git mv src/_archived/xxx src/xxx
2. kubectl set env deployment/awoooi-api USE_NEW_LAYER=false

## 刪除時間
30 天觀察期後可刪除: 2026-04-25
```

---

## 相關文件

- ADR-024: API 分層架構
- ADR-005: BFF Architecture
- ADR-003: leWOOOgo Module Architecture
- `feedback_strangler_fig_pattern.md`
- `reference_phase16_architecture.md`
- `feedback_lewooogo_modular_enforcement.md`
