架構模式的誤用比不用更糟糕。不用架構模式,系統頂多是混亂但可以工作;用錯了,你同時得到混亂和複雜度,還有一個「應該怎麼用」的持續爭論。
以下是最常見的五種誤用,每種都有一個特徵:引入的複雜度遠超出它解決的問題。
1. Clean Architecture 套在 CRUD 小專案上
Clean Architecture 的價值在可測試性和可替換性——domain 不依賴 infrastructure,業務邏輯可以在不起資料庫的情況下測試。
但如果你的「業務邏輯」是 validateNotEmpty(email) 加上 db.insert(user),這個隔離帶來的收益幾乎是零。你換資料庫的頻率是多少?你有多少複雜的 domain rule 需要大量的單元測試?
套上 Clean Architecture 之後,你得到:UserEntity、UserRepository(介面)、PostgresUserRepository(實作)、CreateUserUseCase、UserController——五個 class 做一件三行 code 就能做完的事。新人 onboarding 需要先理解整個依賴圖,才能找到「實際寫入資料庫的是哪一行」。
判斷準則:如果你的系統主要是資料的增刪改查,用三層架構就夠了。Clean Architecture 是業務邏輯足夠複雜時的解法。
2. Event Sourcing 用在不需要歷史的系統
Event Sourcing 的維運清單很長:projection 要建,snapshot 要管,schema 演進要策略,event replay 要測試,event store 要選型和維護。
這些成本在業務歷史重要的系統裡(金融、複雜 workflow、審計需求強的業務)是值得的。在一個「你只需要最新狀態」的系統裡,這些全部是純粹的負擔。
最常見的誤用場景:工程師覺得 audit log 很有用,就引入 Event Sourcing。但如果只是要 audit log,加一張 audit_logs 表或在更新時記錄舊值,成本低一個數量級,功能完全夠用。
判斷準則:如果引入 Event Sourcing 的理由只是 audit trail,那個理由不夠強。需要 replay、時間點查詢、從事件重建不同 projection 才是真正的動機。
3. CQRS 在沒有讀寫衝突的系統
Full CQRS(獨立的 Read Store)帶來 eventual consistency 和 Read Store 同步的維運負擔。這個代價在讀寫需求真的衝突的系統裡是值得的——讀取需要大量 denormalize、讀寫比極度不對稱。
在讀寫需求接近的系統裡,CQRS 帶來的複雜度沒有對應的收益。用戶剛寫入查不到的問題(eventual consistency window)在有些業務場景根本不可接受,而引入 CQRS 卻必須面對它。
Simple CQRS(只是程式碼分開,同一個資料庫)幾乎沒有成本,隨時可以做,不是問題。問題是把 Full CQRS 作為預設選擇。
判斷準則:先用 Simple CQRS 組織程式碼,等到真的出現「讀取效能問題無法靠加 index 解決」或「讀模型需要的 schema 和寫模型完全不同」,再考慮 Full CQRS。
4. 每個 PR 都加一層(layer 膨脹)
一種慢性誤用:每次「這段邏輯放哪裡不確定」,就新增一個層來容納它。Helper、Utils、Manager、Processor、Handler——這些不是架構層,是沒有明確定義的垃圾桶。
六個月後,你有 Service 裡呼叫 Helper、Helper 裡呼叫 Manager、Manager 裡呼叫 Processor、Processor 裡又回呼 Service——一個 business operation 的呼叫鏈深達五層,但沒有一層有清楚的職責邊界。
判斷準則:加新層之前,先確認這一層的職責定義是什麼,和現有的層有什麼明確區別。如果說不清楚,不要加,把邏輯放進最合適的現有層。
5. Saga 在其實可以共用資料庫的服務
Saga Pattern 的存在理由是:多個服務、多個資料庫、跨服務的業務一致性需求。
一個常見的場景:系統用「微服務」的方式部署了幾個小服務,但它們其實都連同一個資料庫。這種情況下,跨服務操作直接用資料庫 transaction 就能解決,不需要 Saga 的補償機制。引入 Saga 增加了事件流設計、補償操作設計、Dead Letter Queue 處理的複雜度,解決的是一個不存在的問題。
判斷準則:Saga 解決的是「跨資料庫的一致性問題」,不是「跨服務的呼叫問題」。如果服務共用同一個資料庫,Saga 不是答案。
所有誤用的共同根源是同一件事:先選工具,再找它能解決的問題。正確的順序是反過來——先確認問題是什麼,再找最小代價的解法。架構模式是解法,不是目標。