架構模式的歷史常被講成一條進步的時間線:「以前用 MVC,後來用 Clean Architecture,現在用微服務加 CQRS」——好像舊的過時了,新的更好。
這個敘述遺漏了最重要的部分:每一代新架構都是因為前一代在特定場景撞牆才出現的。沒有那個牆,就沒有下一代的解法。
MVC(1979):解決 UI 和邏輯混在一起
Smalltalk-80 之前,GUI 應用的所有邏輯——顯示、資料、用戶互動——混在一起。改一個按鈕的顏色可能影響業務計算,修一個業務規則可能破壞畫面更新。
Trygve Reenskaug 的 MVC 把三件事分開:Model 負責資料和邏輯,View 負責呈現,Controller 處理輸入。這讓 UI 和業務邏輯第一次有了明確邊界。
MVC 撞牆的地方:它描述的是「UI 層的分離」,但沒有規範 Model 和 infrastructure(資料庫、外部服務)的關係。隨著 web 應用爆發,MVC 框架(Rails、Django、Spring MVC)的 Model 直接耦合資料庫,業務邏輯和 ORM 查詢混在一起——換資料庫要動業務邏輯,測試業務邏輯要連資料庫。
Layered Architecture(2000 年代):解決 Model 太胖的問題
企業應用開始用三層 / 多層架構把 Model 細分:Presentation Layer、Business Layer、Data Access Layer。業務邏輯終於和資料庫操作分開了。
Layered 撞牆的地方:分層是靠命名慣例和文件維持的,架構本身不阻止上層被下層引用、Business Layer 直接依賴 Data Layer 的具體實作。大型團隊長期協作,這個邊界很難守住。測試 Business Layer 仍然需要資料庫,因為 Business 直接 import 了具體的 Repository 實作。
Hexagonal / Onion(2005–2008):解決依賴方向問題
Alistair Cockburn(Hexagonal,2005)和 Jeffrey Palermo(Onion,2008)都在解同一個問題:Business Layer 為什麼要知道資料庫是什麼?
答案是讓 Business 定義介面(port),Data Access 實作介面(adapter),依賴方向反過來。Business Layer 不依賴 Data Layer,Data Layer 依賴 Business 定義的介面。Domain 可以在不起資料庫的情況下完整測試。
這個解法撞牆的地方:對大型系統,即使 domain 設計好了,還是面臨「讀取需求和寫入需求互相衝突」的問題——normalized 的 Write Model 和 denormalized 的查詢需求難以用一個 model 同時服務。
Clean Architecture(2012):整合前幾代的思想
Robert Martin(Uncle Bob)在 2012 年系統化整理了 Hexagonal / Onion / DCI 等多種思想,提出 Clean Architecture 的同心圓模型,加上明確的四層命名和「依賴規則」。它不是新的發明,是前幾代思想的整合和標準化。
Clean Architecture 沒解決的問題:讀寫模型衝突(CQRS 的問題)、跨服務資料一致性(Saga 的問題)、歷史記錄和 replay(Event Sourcing 的問題)。這些問題在 2012 年之後,隨著微服務架構的普及而變得緊迫。
CQRS / Event Sourcing / Saga(2015+):解決分散式和讀寫衝突
微服務把一個系統拆成多個自治服務,帶來了三個新問題:
- 讀取優化和寫入一致性的衝突:CQRS 把讀模型和寫模型分開,各自最優化
- 狀態歷史的需要:Event Sourcing 把事件序列作為真相來源,而不是當前狀態
- 跨服務事務:Saga 用補償操作取代跨服務的 2PC
這一代撞牆的地方:引入了 eventual consistency 作為常態,讓前端開發和產品設計也必須應對「剛寫入可能查不到」的問題。這是下一代可能要解決的問題。
每一代架構都是當時問題的解法,也創造了下一個問題。理解這條演進線,最實用的地方是:當你考慮引入一個模式,先確認你是否真的遇到了它設計來解決的那個牆。沒有牆,就不需要那個解法。
上一篇 → 為什麼要有架構模式