本章的核心論點是:Model 與 Implementation 必須緊密綁定。如果 domain model 與程式碼之間脫節,model 將淪為無用的文件,而程式碼也將失去概念上的清晰度。Evans 提出 Model-Driven Design 作為解決方案,並透過 PCB 佈線工具的實際案例,展示從程序式腳本到模型驅動設計的轉變。
Model-Driven Design#
Analysis Model 的陷阱#
許多專案採用一種常見但有問題的方法:由分析師建立 analysis model,再由開發者另外建立 design。這兩者之間往往只有鬆散的對應關係,造成嚴重後果:
- 知識流失:分析階段透過 knowledge crunching 獲得的洞見,在進入實作時大量流失,因為開發者被迫重新建立自己的抽象
- 模型過時:一旦開始寫程式,analysis model 很快就被拋棄,因為它不考慮實作可行性
- 洞見斷裂:設計/實作過程中總會出現預料之外的發現,但這些發現無法回饋到 analysis model 中
- 分析不足:analysis model 會深入某些不相關的主題,卻忽略真正重要的議題
如果 design(或其核心部分)無法對應到 domain model,那麼 model 幾乎沒有價值,軟體的正確性也令人存疑。反之,model 與 design 之間過於複雜的對映關係同樣無法維護。
Model-Driven Design 的核心主張#
Model-Driven Design 拋棄 analysis model 與 design model 的二分法,追求一個同時滿足兩種目的的單一模型:
- 每個 design 中的物件都扮演 model 中描述的概念角色
- 從 model 中提取 design 所用的術語與職責分配
- 程式碼成為 model 的表達——改變程式碼就等於改變 model,其影響必須波及整個專案
- Modeling 與 design 的過程合併為單一的迭代循環
設計軟體系統的某個部分,使其以非常直白的方式反映 domain model。同時也要修正 model,使其能更自然地在軟體中實現——即使在這個過程中,仍然要持續追求對 domain 更深層的理解。
flowchart TD
subgraph traditional["傳統 Analysis Model"]
direction LR
A1["領域知識"] -.-> A2["Analysis Model"]
A2 -.-> A3["Design Model"]
A3 -.-> A4["Code"]
end
subgraph mdd["Model-Driven Design"]
direction LR
B1["領域知識"] <--> B2["單一模型"]
B2 <--> B3["Code"]
end
traditional ~~~ mdd
style traditional fill:#fff3e0,stroke:#e65100
style mdd fill:#e8f5e9,stroke:#2e7d32這個方法對模型的要求更高:它必須同時是良好的分析工具和可行的設計基礎。當一個 model 不適合實作時,就必須尋找新的 model;當 model 無法忠實表達 domain 的關鍵概念時,同樣必須重新尋找。
Modeling Paradigms and Tool Support#
要讓 Model-Driven Design 發揮價值,model 與 design 之間的對應必須是精確的、字面上的。這幾乎必然要求使用支援特定 modeling paradigm 的語言和工具。
Object-Oriented Programming#
Object-oriented programming 之所以強大,是因為它本身就基於一種 modeling paradigm:
- 物件確實存在於記憶體中,擁有與其他物件的關聯
- 物件被組織成 class,提供可透過訊息呼叫的行為
- Java 等語言允許直接建立與概念物件模型相類比的物件與關係
OOP 的真正突破不在於用物件來組織程式碼,而在於程式碼表達了 model 的概念。
Procedural Languages 的限制#
純粹的 procedural language(如 C)難以支援 Model-Driven Design,因為:
- 程式只是一系列技術性的資料操作步驟,無法捕捉 domain 的意義
- 雖然支援複雜資料型別,但這些只是組織化的資料,無法捕捉 domain 的主動面向
- 功能之間是基於預期的執行路徑連結,而非基於 domain model 中的概念關聯
Evans 提到 Prolog 作為另一個適合 Model-Driven Design 的例子——其 paradigm 是邏輯推理,model 則是一組邏輯規則與事實。重點不在於特定語言,而在於語言是否支援某種 modeling paradigm。
範例:從 Procedural 到 Model-Driven#
Evans 以 PCB(printed circuit board)佈線工具為例,展示兩種截然不同的設計方式。
問題背景#
PCB 上有數以萬計的 net(電氣導體連接),每條 net 都有自己的佈線規則。工程師將多條 net 視為屬於同一個 bus(例如 8、16 或 256 條 net 組成一組),希望能以 bus 為單位指派規則。但佈線工具沒有 bus 的概念,規則必須逐條 net 指派。

Figure 3.2: An explanatory diagram of buses and nets
Mechanistic Design(程序式腳本)#
工程師撰寫腳本來解決這個問題,其做法是:
- 依 net name 排序 net list 檔案
- 利用命名慣例(名稱字首相同的 net 屬於同一個 bus),逐行解析檔案
- 將 bus 規則展開,逐條寫入每條 net 的 rules 檔案
這種做法的問題:
- 只是檔案操作,沒有明確處理 bus 的概念
- 換一種檔案格式就必須從頭來過
- 要加入更豐富的功能或互動性,代價極高
- 唯一的測試方式是端對端的檔案比對
Model-Driven Design#
將 domain 概念明確地組織成模型,以物件導向語言實作:

Figure 3.3: A class diagram oriented toward efficient assignment of layout rules
核心實作變得幾乎是 trivial 的:
AbstractNet持有 rules,提供assignRule()與assignedRules()Net繼承AbstractNet,其assignedRules()合併自身規則與所屬 Bus 的規則Bus同樣繼承AbstractNet,可以直接指派規則
搭配的 services 與 utilities:
| 元件 | 職責 |
|---|---|
| Net List Import Service | 讀取 net list 檔案,為每筆資料建立 Net 實例 |
| Net Rule Export Service | 將所有 Net 的規則完整展開寫入 rules 檔案 |
| Net Repository | 依名稱存取 Net |
| Inferred Bus Factory | 利用命名慣例從 Net 集合推斷出 Bus,建立實例 |
| Bus Repository | 依名稱存取 Bus |
這種設計的優勢:
- 概念明確:Bus 作為一個明確的 domain object 存在,而非透過排序和字串比對推斷
- 可測試:每個元件都有明確定義的介面,可進行 unit test;核心 domain logic 也可獨立測試
- 可擴展:Model-Driven Design 可以輕鬆加入規則組合的 constraints 或其他增強功能
- 格式無關:核心邏輯不依賴特定檔案格式
這樣的設計不會一步到位。它需要多次迭代的 refactoring 與 knowledge crunching,才能將 domain 中的重要概念提煉為簡潔而深刻的 model。
Letting the Bones Show:為什麼 Model 對使用者也重要#
Evans 以 Internet Explorer 的「我的最愛」(Favorites)功能為例,說明 user model 與 implementation model 不一致時的問題:
- 使用者認為 Favorite 是一個網站名稱的清單
- 實際上 Favorite 是一個包含 URL 的檔案,檔名就是顯示的名稱
- 當網頁標題包含 Windows 檔名不允許的字元(如
:)時,系統會顯示令人困惑的錯誤訊息,或靜默移除這些字元
解決方式有兩種,但都指向同一個原則——維持單一模型:
- 暴露真實模型:讓使用者知道 Favorites 就是捷徑檔案,使用者可以利用對檔案系統的既有認知來管理
- 改變實作模型:用資料檔案儲存 Favorites,讓它遵守網頁命名規則而非檔案命名規則
當 design 基於一個反映使用者與 domain experts 核心關切的 model 時,這個 design 的「骨架」可以更大程度地暴露給使用者,帶來一致且可預測的行為。
Hands-On Modelers#
Evans 挑戰了軟體開發中常見的「製造業隱喻」——由高技能工程師設計,低技能工人組裝。他指出:軟體開發全部都是設計。
Evans 的親身經歷#
Evans 描述了一個他負責協調多個應用團隊並開發 domain model 的專案:
- 管理層認為 modeler 就應該做 modeling,寫程式是浪費技能,因此禁止他參與實作
- 他與 domain experts 和各團隊開發主管合作,提煉出一個精良的核心 model
- 但這個 model 從未被真正落實,原因有二:
- 意圖在交接中流失:model 的整體效果對細節非常敏感,而這些細節無法僅靠 UML 圖或一般性討論傳達
- 回饋太間接、太慢:model 在特定技術平台上效率極差的問題,隔了好幾個月才傳回給他;等到那時,開發者已經拋棄 model,自行寫出能運作的程式
分離 Modeler 與 Implementation 的危害#
- 寫程式的人如果不覺得自己對 model 負責,或不知道如何讓 model 為應用服務,那麼 model 就與軟體無關
- 開發者如果不意識到修改程式碼等於修改 model,其 refactoring 將削弱而非強化 model
- 脫離實作的 modeler 會失去對實作限制的直覺,產出不切實際的 model
- 資深設計者的知識與技能無法傳遞給其他開發者
Hands-On Modelers 並不意味著團隊成員不能有專門的角色。問題出在將 modeling 與 implementation 這兩個在 Model-Driven Design 中緊密耦合的任務強行分離。任何寫程式碼的人都必須在某種程度上參與 modeling,任何負責 modeling 的人也必須參與到程式碼中。