Clean Architecture:為什麼商業邏輯不該被框架與資料庫綁死

Clean Architecture 的核心,不是把程式切成很多層,而是控制依賴方向:商業規則可以被框架、資料庫與介面呼叫,但不應反過來依賴它們。 當核心邏輯能獨立於外部技術存在,系統才比較容易測試、修改,也比較不會因為換框架、換資料庫或換介面,就被迫重寫整套商業邏輯。

很多系統剛開始都很簡單。

先選一個框架、建資料表、做幾個頁面,再把商業規則寫進 Controller、Model 或某個 Service 裡。功能不多時,這樣做很快,也不一定有問題。

真正的麻煩通常出現在系統長大之後。

有一天你想換資料庫,才發現價格計算、會員資格、訂單狀態全部直接依賴 ORM;想新增 API,才發現商業邏輯散落在網頁 Controller;想做自動化測試,又發現每次測一個規則都必須先啟動整個網站與資料庫。

這些症狀表面看起來不一樣,底層常是同一件事:重要的商業規則被外部技術綁住了。

Robert C. Martin 提出的 Clean Architecture,就是試著把這個依賴方向重新整理清楚。

Clean Architecture 真正要解決的是什麼?

軟體系統裡有兩類東西。

一類是你的事業真正要做的事,例如「什麼條件可以購買」「怎麼計算價格」「誰可以觀看課程」「退款條件怎麼判斷」「訂單完成後要做哪些事」。

另一類是把這些事情實作出來的技術,例如 WordPress、Laravel、React、MySQL、Stripe、WooCommerce、REST API、Email 服務。

前者是政策,後者是細節。

如果今天把 MySQL 換成另一種資料庫,「會員什麼條件才能看到課程」這個規則理論上不應該改變。資料存在哪裡變了,商業規則沒有變。

如果網站前端從傳統頁面換成 App,「退款超過幾天不能申請」也不應該跟著重寫。

Clean Architecture 的目的,就是讓這些比較穩定、比較重要的規則,不要被變動比較快的外部技術牽著走。

依賴規則:所有原始碼依賴都應該往內

Clean Architecture 最核心的一句話,是依賴規則(Dependency Rule):原始碼的依賴方向應該往內,指向更高層的政策。

經典示意圖通常會畫成幾個同心圓,由內到外大致是:

Entities → Use Cases → Interface Adapters → Frameworks and Drivers。

這四層不是硬性規定,也不是說每個專案一定要有四個資料夾。真正不能破壞的是方向。

內層不應該知道外層的具體技術名稱。

例如 Use Case 不應直接知道「這份資料是從 MySQL 來的」,也不應該知道「這個請求是 HTTP POST」。它只需要知道自己收到了什麼資料、要執行什麼規則、最後要交回什麼結果。

外層可以知道內層,內層不要知道外層。

Entities:不是資料庫資料列,而是核心商業規則

Entities 常被誤解成「資料庫裡的 Model」。但在 Clean Architecture 裡,Entity 的重點不是資料怎麼存,而是它承載的核心規則。

假設你有一套課程會員系統。

「購買年繳方案後可以使用 365 天」「付款完成才啟用權限」「已退款訂單不再保有課程資格」,這些都屬於事業本身的規則。

它們不應該因為今天使用 WooCommerce、明天改成別的電商系統,就全部重寫。

這就是 Entity 應該保護的東西。

資料庫資料列只是儲存方式。Entity 是這些資料代表的商業意義與規則。

Use Cases:把一次完整的商業行為說清楚

Use Case 是應用程式層的商業行為。

它處理的是「使用者要完成什麼事情」。

例如:

建立訂單、啟用會員、取消訂閱、申請退款、建立課程、發布內容、寄出付款完成通知。

Use Case 會協調 Entity 與外部資料,但自己不應被 HTTP、資料庫或某個框架的格式綁死。

例如「啟用會員」這個 Use Case,可以收到一個單純的輸入資料:會員 ID、商品 ID、付款狀態。

至於這些資料是從 WordPress 表單來、API 來、後台按鈕來,對 Use Case 而言不重要。

這樣做最大的好處,是同一套商業規則可以被不同入口重複使用。

Interface Adapters:把外面的語言翻成裡面懂的語言

外部世界不會剛好長成你的商業規則需要的樣子。

HTTP Request 有自己的格式,資料庫有自己的欄位,第三方 API 也有自己的 JSON 結構。

Interface Adapters 的工作,就是轉換。

Controller 把 HTTP 請求轉成 Use Case 可以理解的輸入;Repository Adapter 把資料庫資料轉成核心可以使用的物件;Presenter 再把 Use Case 的結果轉成網頁、JSON 或其他介面需要的格式。

這一層很像翻譯。

如果翻譯做得好,核心商業邏輯就不用學每一個外部系統的語言。

Frameworks and Drivers:重要,但仍然是外層細節

框架、資料庫、Web Server、UI、第三方 SDK 都非常重要。

Clean Architecture 並不是說它們不重要,而是說它們不應該決定整個系統的核心形狀。

Robert C. Martin 在談框架時,一直提醒開發者:框架會希望你按照它的方式組織程式。這對快速開發很方便,但代價是你的程式可能愈來愈難離開它。

所以比較健康的做法,是讓框架扮演外層工具。

框架負責收 HTTP、處理路由、建立物件、連接資料庫,再把事情交給內層。

商業規則不需要知道框架怎麼運作。

最難理解的一點:控制流程可以往外,依賴仍然往內

這是 Clean Architecture 最常讓人卡住的地方。

商業邏輯明明要呼叫資料庫,為什麼說它不能依賴資料庫?

答案是依賴反轉(Dependency Inversion)。

內層先定義自己需要什麼能力,例如:

「我需要一個可以取得會員資料的介面。」

至於這個介面最後是 MySQL、PostgreSQL、REST API 還是其他服務實作,是外層的事情。

執行時,Use Case 的確會呼叫一個 Repository;但在原始碼依賴上,外部 Repository 實作反而依賴內層定義的介面。

也就是說,控制流程可能是「核心呼叫資料存取」,但程式依賴方向仍然是「資料存取實作依賴核心規格」。

這個反轉,就是讓資料庫從「主人」變成「插件」的關鍵。

資料跨越邊界時,不要把外層格式直接帶進核心

就算依賴方向畫對了,如果資料格式仍然一路穿透,耦合還是會發生。

例如直接把 ORM Model 傳進 Use Case,看起來省事,但核心邏輯就開始知道 ORM 的欄位、Lazy Loading、資料關聯與生命週期。

比較乾淨的方式,是在邊界做轉換。

Use Case 收到的是簡單的 Request Model,回傳的是簡單的 Response Model。資料庫 Model、HTTP Request、Framework Object 留在外層。

這樣即使外部格式改變,真正受到影響的也主要是 Adapter。

Clean Architecture 不等於「層愈多愈專業」

這套方法很容易被用過頭。

有人看完架構圖後,把每一個功能拆成 Controller、Service、UseCase、Repository、Gateway、Adapter、Presenter、DTO,再加一堆介面。

結果一個簡單功能要跳十個檔案才看得懂。

這不叫乾淨,這叫新的複雜度。

Clean Architecture 自己的核心原則從來不是「層數愈多愈好」。真正要問的是:這個邊界值不值得存在?它隔離的是不是一個真的可能變動、真的值得保護的細節?

如果只是很小、很穩定的功能,不一定需要完整邊界。

架構的價值來自降低未來變更成本,不是增加今天的儀式感。

資料庫為什麼應該被視為細節?

對很多系統來說,資料庫看起來像最核心的東西,因為所有資料都放在裡面。

但從商業規則角度看,資料庫只是保存狀態的方法。

例如「訂單付款後啟用一年會員資格」這件事,不會因為會員資料存在 MySQL 就變成 MySQL 規則。

當商業邏輯直接寫在 SQL、Stored Procedure、ORM Hook 或 Active Record Model 裡,資料庫與規則就會愈綁愈緊。

短期看起來很方便,長期卻會讓測試、遷移與重構變得困難。

把資料庫放到外層,不是否定資料的重要性,而是把「資料的意義」和「資料的儲存技術」分開。

框架也是一樣:不要讓網站看起來像框架目錄

一個系統的結構,最好讓人一眼看出它在做什麼。

如果你打開專案,只看到 Controllers、Models、Views、Middleware、Providers,卻看不出這是一個課程平台、訂閱系統還是預約系統,代表框架正在主導你的架構。

更好的方向,是讓程式結構反映商業能力,例如 Orders、Memberships、Courses、Payments、Bookings。

框架仍然存在,但它應該在邊緣支援這些能力,而不是讓商業概念被框架資料夾拆散。

Clean Architecture 跟微服務沒有直接等號

另一個常見誤解,是以為「拆成微服務」就代表架構乾淨。

不一定。

如果五個微服務共同依賴同一份混亂資料模型、互相呼叫形成循環、每改一個欄位就要五個服務一起部署,那只是把原本的耦合搬到網路上。

相反地,一個模組邊界清楚的單體系統,也可能擁有很好的架構。

部署單位和依賴方向是兩個不同問題。

先把模組與商業邊界想清楚,再決定是否需要微服務,通常比「先拆服務再說」安全。

放到 WordPress、課程網站與外掛開發,可以怎麼理解?

這套概念其實很適合 WordPress 客製功能。

例如你要做一套「付款後開通課程權限」的功能。

最容易的寫法,是直接把 WooCommerce Order、WordPress User、課程外掛 API 全部寫在同一段 Hook 裡。

功能能跑,但商業規則和三套外部系統完全綁死。

比較乾淨的做法,是先把核心規則整理成「什麼付款狀態可以開通」「開通哪一個權限」「有效期限怎麼算」。

WooCommerce Hook 只是負責接收事件,轉成核心需要的資料;WordPress 或課程系統 API 則放在 Adapter 裡,負責真正寫入權限。

未來就算金流、課程外掛或會員系統換掉,核心規則仍然可以保留。

如果你的系統目前已經慢、難改、邊界混亂,也可以先看上一篇 系統愈做愈慢、愈改愈亂?架構最佳化應該先從哪裡開始。那篇處理的是「既有系統該怎麼整理」,這篇則更聚焦在依賴方向與架構邊界。

什麼情況值得建立完整邊界?

不是每一段程式都需要完整 Clean Architecture。

比較值得畫邊界的地方,通常有幾個特徵。

第一,商業規則重要,而且很可能長期存在。

第二,外部技術容易變,例如金流、第三方 API、資料庫、訊息服務。

第三,這段邏輯需要被不同介面重複使用,例如網站、App、CLI、排程工作都會呼叫。

第四,這段功能很難測,而困難主要來自外部依賴。

第五,一個小修改經常牽動很多無關程式。

如果完全沒有這些問題,就不必為了「架構漂亮」強行增加層次。

實際做法應該從最容易變、最難測、最常出錯的邊界開始,而不是一次把整個專案重新分層。這樣才能讓架構投資真正換回維護效率,而不是只增加新的抽象。

真正的判斷標準:核心能不能獨立存在?

想快速檢查自己的架構,可以問幾個問題。

商業規則能不能在不啟動 Web Server 的情況下測試?

換資料庫時,核心邏輯要不要跟著改?

Use Case 是否知道 HTTP、WordPress、Laravel、WooCommerce 或其他框架物件?

外部服務是否透過介面接入,而不是散落在核心各處?

一個功能的商業規則是否集中在能被理解的位置?

如果大多數答案都是否定的,那真正需要整理的可能不是程式碼格式,而是依賴方向。

Clean Architecture 的價值,是把未來的選擇權留下來

好的架構不是為了證明工程師懂很多模式。

它的價值是:當外部世界變動時,你不必一起重寫最重要的商業規則。

框架會更新,資料庫會更換,第三方 API 會改版,前端介面也可能重做。這些變化幾乎無法避免。

真正值得保護的,是你的產品為什麼這樣運作、客戶有哪些權利、訂單怎麼處理、服務如何交付。

當依賴方向正確,外部技術可以替換,核心規則仍然站得住。

這才是 Clean Architecture 最實際的價值。

常見問題

Clean Architecture 一定要有四層嗎?

不一定。四個同心圓是常見的說明方式,但真正固定的是依賴規則:外層可以依賴內層,內層不要依賴外層。實際層數可以依系統規模調整。

Entity 就是 ORM Model 嗎?

不是。ORM Model 主要處理資料持久化;Clean Architecture 裡的 Entity 承載核心商業規則。兩者可以有對應關係,但不應直接視為同一件事。

資料庫真的可以完全換掉而不用改任何程式嗎?

實務上通常仍要修改外層 Adapter、查詢與遷移程式。Clean Architecture 的目標不是「零修改」,而是讓核心商業規則不必因資料庫更換而一起重寫。

小型網站也需要 Clean Architecture 嗎?

不一定需要完整做法。小型、短期、規則簡單的功能可以保持簡單;但付款、會員、權限、訂閱等重要而容易變動的邏輯,提早建立清楚邊界通常比較值得。

用了框架就一定違反 Clean Architecture 嗎?

不是。問題不在使用框架,而在核心邏輯是否被框架綁死。框架很適合處理路由、輸入輸出、資料連線等外層工作,只要依賴方向控制得好,一樣可以使用。

參考資料

Robert C. Martin. Clean Architecture: A Craftsman’s Guide to Software Structure and Design.
Robert C. Martin. “The Clean Architecture.” The Clean Code Blog.
Robert C. Martin. “A Little Architecture.” The Clean Code Blog.
Robert C. Martin. “Framework Bound.” The Clean Code Blog.
Robert C. Martin. “The Single Responsibility Principle.” The Clean Code Blog.

元氣站長
元氣站長

經營網路事業,賺取持續收入!
網路賺錢修煉,成就理想人生!