本章探討如何在大型系統中建立 Large-Scale Structure(大規模結構),讓開發者能在不深入每個細節的前提下,理解各個部分在整體中的角色。Evans 提出了五種模式:Evolving Order、System Metaphor、Responsibility Layers、Knowledge Level 與 Pluggable Component Framework,各自適用於不同成熟度與複雜度的專案。
為什麼需要 Large-Scale Structure#
隨著系統規模擴大,即使已經用 MODULE 分解設計、用 BOUNDED CONTEXT 做嚴格隔離、用 Distillation 聚焦 CORE DOMAIN,仍然可能面臨以下問題:
- MODULE 數量龐大,開發者不知道該在哪個 package 找特定功能
- 新的 class 不知道該放在哪裡
- 不同開發者各自做出合理但風格各異的設計決策,整體拼湊後難以理解
- CONTINUOUS INTEGRATION 逐漸崩壞,BOUNDED CONTEXT 開始碎裂
Large-Scale Structure 是一種讓你能以「大筆觸」討論和理解系統的語言。它建立一套高階概念或規則,為整個系統提供設計模式,指引設計同時幫助理解。
Evans 以衛星通訊模擬器為開場案例:團隊雖然已有良好的 MODEL-DRIVEN DESIGN 與 MODULE 分解,但隨著複雜度提升,需要一種能反映領域本質的整體結構。他們最終發現以通訊系統的層次(物理基礎設施、封包路由等)來組織設計,讓整個系統回到可理解的狀態。
核心原則:制定一套規則或角色關係,橫跨整個系統,讓人即使不了解某部分的細節,也能理解它在整體中的位置。

Figure 16.1: Some patterns of large-scale structure
Evolving Order#
許多開發者經歷過無結構設計的代價。為了避免混亂,專案往往施加架構來約束開發。但 Evans 指出,這種做法有其危險:
- 過早凍結的架構會在需求變化與理解加深時變成束縛
- 開發者會為了配合架構而簡化應用程式(dumbing down),或乾脆繞過架構
- 問題不在於指導規則的存在,而在於規則的僵化程度與來源
讓大規模概念結構隨應用程式演進,途中可能完全改變結構類型。不要用全域規則過度約束那些需要在地知識才能做出的詳細設計與模型決策。
演進的關鍵考量#
- 個別與整體的妥協:選擇 Large-Scale Structure 是偏好整體可管理性,可能犧牲個別部分的最佳結構。良好的結構會讓這種妥協最小化
- 跨 BOUNDED CONTEXT 適用:結構通常需要跨越多個 Context,不能過度綁定某個特定模型
- 容納實務限制:設計者可能無法控制某些子系統(如外部或遺留系統),結構必須有足夠彈性
- 可選而非必要:與 CONTEXT MAP 不同,Large-Scale Structure 是可選的。只有在好處大於代價、且找到合適結構時才應施加
- 少即是多:不要追求全面性,找到能解決已浮現問題的最小集合即可
Large-Scale Structure 可以容許少數例外,但這些例外必須被標示出來。如果例外開始變多,結構就需要修改或捨棄。
System Metaphor#
System Metaphor 是 Extreme Programming 的核心實踐之一,利用具體的類比來為整個系統的開發帶來秩序。
Evans 用 Firewall(防火牆)為例:這個隱喻影響了網路架構、塑造了整個產品類別,讓新手也能迅速掌握概念。但所有隱喻都帶有包袱——防火牆隱喻導致開發出的軟體屏障有時過度限制了合理的交換,卻對牆內的威脅(如無線區域網路的漏洞)毫無防護。
運用方式#
當出現一個具體類比能抓住團隊成員的想像力,且引導思維朝有用的方向發展時:
- 將它採納為 Large-Scale Structure
- 將設計圍繞此隱喻組織
- 將它吸收進 UBIQUITOUS LANGUAGE
- 持續檢視隱喻是否被過度延伸或不再適切,準備好在它造成阻礙時捨棄
Evans 認為所謂的 “Naive Metaphor”(即用領域模型本身當作隱喻)這個術語應該被廢除。成熟的領域模型是經過多次 knowledge crunching 迭代、且被實作驗證過的產物,絕非「天真」。SYSTEM METAPHOR 並非所有專案都需要,UBIQUITOUS LANGUAGE 本身就能扮演類似角色。
Responsibility Layers#
本章篇幅最長的部分。當你深入理解一個領域後,會開始看到廣泛的模式:某些概念和活動發生在其他元素的背景之上,而這些背景元素以不同的速率、因不同的原因而變化。這種自然的分層暗示了 Responsibility Layers。
核心概念#
- Layer 的定義:系統的分區,每個分區的成員可以感知並使用「下方」層的服務,但不感知也不依賴「上方」層
- 結合了兩個強大的原則:Layering(分層)與 Responsibility-Driven Design(責任驅動設計)
- 使用 Relaxed Layered System 變體:允許元件存取任何更低的層,而不僅限於緊鄰的下一層

Figure 16.2: Ad hoc layering: What are these packages about?
審視模型中的概念依賴關係,以及不同部分的變化速率與變化來源。如果識別出領域中的自然層次,就將它們塑造為廣泛的抽象責任。這些責任應該講述系統高階目的與設計的故事。重構模型,使每個 domain object、AGGREGATE 與 MODULE 的責任都整齊地落入某一層的責任範圍。
範例:貨運系統的分層#
Evans 用貨運系統的擴展範例詳細展示了 Responsibility Layers 的發現與應用過程。
初始觀察:團隊注意到概念有自然的分層——可以在不提及貨物的情況下討論運輸排程,但很難在不提及運輸工具的情況下討論貨物追蹤。

Figure 16.3: A basic shipping domain model for routing cargoes

Figure 16.4: Using the model to route a cargo during booking
第一輪:Operations 與 Capability 兩層
- Operations 層:公司的活動(過去、現在、計畫中的)。Cargo、Route Specification、Itinerary 屬於此層
- Capability 層:公司執行營運所仰賴的資源。Transit Leg 是典型的 Capability 物件
關於 Customer 的歸屬是個較棘手的決策。對一次性包裹寄送服務,客戶可能只是營運層的關注;但對培養長期關係的航運公司,Customer 屬於 Capability(Potential)層。這不是技術決策,而是對領域知識的捕捉與傳達。
由於 Cargo 與 Customer 之間的關聯只能單向遍歷,Cargo REPOSITORY 需要一個查詢來找出特定 Customer 的所有 Cargo。分層結構的施加使這成為一項要求。

Figure 16.5: A query replaces a bidirectional association that violates the layering.

Figure 16.6: A first-pass layered model
第二輪:加入 Decision Support 層
經過數週實驗,團隊注意到 Router 等元素不屬於當前的營運現實或計畫,而是幫助做出改變計畫的決策。團隊定義了新的 “Decision Support” 層。
但發現 Transit Leg 上的 “is preferred” 屬性不一致——它代表的是引導決策的政策,而非 Capability。因此需要重構模型:將 Route Bias Policy 獨立出來,讓 Transit Leg 更專注於運輸能力的基本概念。

Figure 16.7: Refactoring the model to conform to the new layering structure
基於對領域深入理解的 Large-Scale Structure 往往會推動模型朝著闡明其含義的方向發展。

Figure 16.8: The restructured and refactored model
結構如何影響後續設計#
一旦採用了 Large-Scale Structure,後續的建模和設計決策都必須將它納入考量。Evans 用新增危險品路由限制的功能來說明。
一種直覺的設計是讓 Cargo 負責整合 HazMat 路由規則,但這違反了分層結構——Cargo(Operations 物件)不應依賴 HazMat Route Policy Service(Decision Support 物件)。

Figure 16.9: A possible design for routing hazardous cargo
符合分層的替代方案:讓 Router 負責在搜尋路線前收集適當的政策,修改 Router 的介面以納入政策可能依賴的物件。

Figure 16.11: A design consistent with layering

Figure 16.12: A typical interaction for routing hazardous cargo
兩種設計各有優缺點。但如果專案中所有人以一致的方式做決策,整體設計的可理解性遠比個別設計選擇的微小取捨更有價值。如果結構迫使太多彆扭的設計選擇,依照 EVOLVING ORDER 原則,應評估並可能修改或捨棄它。
flowchart TD
V1["第一輪\nOperations + Capability"] --> Q1{"Router 屬於哪層?\nCustomer 屬於哪層?"}
Q1 --> V2["第二輪\n加入 Decision Support"]
V2 --> Refactor["重構:分離\nRoute Bias Policy\n從 Transit Leg"]
Refactor --> V3["三層結構確立\nOperations / Decision Support / Capability"]
V3 --> NewReq["新需求:Hazmat Routing"]
NewReq --> Fix["修正:Router 先收集\n所有 Policy 再計算"]選擇合適的 Layers#
好的 Responsibility Layers 應具備以下特徵:
- Storytelling(敘事性):層次應傳達領域的基本現實或優先順序,這是業務建模決策而非技術決策
- Conceptual Dependency(概念依賴):上層概念要在下層的背景下才有意義,下層概念則可獨立存在
- Conceptual Contours(概念輪廓):不同層的物件若有不同的變化速率或變化來源,層次就能容納它們之間的剪切
常見的 Responsibility Layers#
Evans 歸納了幾種在相關領域家族中反覆出現的層次:
| Layer | 核心問題 | 說明 |
|---|---|---|
| Potential(潛力) | 可以做什麼? | 組織的資源及其組織方式。在運輸、製造等大型固定資本投資的企業中特別突出 |
| Operations(營運) | 正在做什麼? | 反映實際情況而非期望。Operations 物件通常會引用甚至由 Potential 物件組成,但反過來不行 |
| Decision Support(決策支援) | 應該採取什麼行動或設定什麼政策? | 基於下層資訊進行分析和決策 |
| Policy(政策) | 規則和目標是什麼? | 大多是被動的,但約束其他層的行為。Policy 與 Decision Support 搭配效果良好 |
| Commitment(承諾) | — | 在金融服務、保險等領域中浮現,兼具 Policy 的性質(指引未來營運)和 Operations 的性質(隨業務活動出現和改變) |

Figure 16.13: Conceptual dependencies and shearing points in a factory automation system

Figure 16.14: Conceptual dependencies and shearing points in an investment banking system
層次數量最好保持簡潔,超過四或五層就會變得笨重。太多層無法有效地講述故事,大規模結構原本要解決的複雜度問題會以新的形式回歸。大規模結構必須被狠狠地提煉。
Knowledge Level#
Knowledge Level 源自 Martin Fowler 的 Analysis Patterns,定義為「一組描述另一組物件應如何行為的物件」。它在我們需要讓模型的某些部分可由使用者調整、但又受到更廣泛規則約束時,能解開複雜性。
問題場景#
以組織中的「accountability」(問責制)建模為例:
- 不同組織有不同的角色和關係規則(部門由 Director 領導 vs. 模組由 Manager 領導)
- 典型應用程式會做出一些假設,不符合時使用者開始以非預期的方式使用資料輸入欄位
- 完全靜態的模型會造成問題,但完全彈性的系統同樣不好——不方便使用且無法執行組織自身的規則
核心思想#
Knowledge Level 是將 REFLECTION 模式應用於領域層。它把軟體分成:
- Operations Level(營運層級):承載應用程式營運責任的基本物件
- Knowledge Level(知識層級):代表軟體結構和行為知識的物件,可由使用者或超級使用者自訂
| Fowler 術語 | POSA 術語 |
|---|---|
| Knowledge Level | Meta Level |
| Operations Level | Base Level |
建立一組獨特的物件,用於描述和約束基本模型的結構與行為。將這些關注點分為兩個「層級」:一個非常具體,另一個反映使用者或超級使用者可以自訂的規則和知識。
程式語言的 reflection 工具不是用來實作領域模型的 Knowledge Level。那些 meta-object 描述的是語言構造本身的結構和行為。Knowledge Level 必須由普通的領域物件構建。
範例:員工薪資與退休金#
Part 1:問題浮現
原有模型將薪資類型(hourly/salary)與退休金計畫(defined-benefit/401k)綁定在一起。

Figure 16.15: The old model, overconstrained for new requirements

Figure 16.16: Some employees represented using the old model
當管理層決定辦公室行政人員應加入 defined-benefit 退休金計畫時,原模型不允許混合搭配。
第一個提案是簡單地移除限制,但管理層拒絕了,因為它無法執行公司政策——任何人都可能被錯誤地指派到任何計畫。

Figure 16.17: The proposed model, now underconstrained

Figure 16.18: Employees can be associated with the wrong plan.
解決方案是將 “job title” 欄位重構為明確的 Employee Type 概念:

Figure 16.19: The Type object allows requirements to be met.

Figure 16.20: Each Employee Type is assigned a Retirement Plan.
Part 2:辨識 Knowledge Level
開發者注意到某些物件被限制編輯(只有超級使用者能修改),而其他物件可自由編輯。被限制的物件群恰好構成了 Knowledge Level。

Figure 16.21: Recognizing the KNOWLEDGE LEVEL implicit in the existing model
辨識出 Knowledge Level 後,團隊發現 Employee Type 其實混合了兩個概念。在 UBIQUITOUS LANGUAGE 中的表述——「An Employee Type is assigned to either Retirement Plan or either payroll」——暴露了一個隱含概念:Payroll 在模型中並不存在,它與 Employee Type 被混為一談。
重構後,Payroll 成為獨立概念:

Figure 16.22: Payroll is now explicit, distinct from Employee Type.

Figure 16.23: Each Employee Type now has a Retirement Plan and a Payroll.
Knowledge Level 與 Responsibility Layers 的關係#
- Knowledge Level 乍看像是 Responsibility Layers 中 Policy 層的特例,但其實不是
- 關鍵區別:Knowledge Level 的依賴是雙向的(兩個層級相互依賴),而 Layers 中下層獨立於上層
- Knowledge Level 可以與大多數其他 Large-Scale Structure 共存,提供額外的組織維度
Knowledge Level 應謹慎使用。如果 Knowledge Level 變得複雜,系統行為對開發者和使用者來說都會難以理解。配置它的使用者最終可能需要程式設計師的技能。只在客製化至關重要、且否則會扭曲設計的地方才應用。
Pluggable Component Framework#
Pluggable Component Framework 通常只在模型非常成熟——深入且經過提煉——之後才會出現,且多半是在同一領域已有數個應用程式被實作之後。
適用情境#
當多個應用程式必須互通、都基於相同的抽象但獨立設計時:
- 多個 BOUNDED CONTEXT 之間的翻譯限制了整合
- SHARED KERNEL 對於不密切合作的團隊不可行
- 重複和碎片化提高了開發與部署成本,互通性變得非常困難
做法#
- 提煉出介面與互動的 ABSTRACT CORE,建立一個允許這些介面的多種實作被自由替換的框架
- 同樣允許任何應用程式使用這些元件,只要它嚴格透過 ABSTRACT CORE 的介面操作
- 高階抽象在系統的廣度上被識別和共享;特化發生在 MODULE 中
- 應用程式的中心樞紐是 SHARED KERNEL 中的 ABSTRACT CORE
權衡#
- 困難度高:需要精確的介面設計和足夠深入的模型來在 ABSTRACT CORE 中捕捉必要行為
- 應用受限:如果應用程式需要非常不同的 CORE DOMAIN 方法,結構會成為阻礙
- 凍結 CORE 的持續精煉:開發者可以特化模型,但無法在不改變所有元件協定的情況下修改 ABSTRACT CORE
Pluggable Component Framework 不應是專案中首先採用的 Large-Scale Structure,甚至不應是第二個。最成功的案例都是在多個專門應用程式完整開發之後才出現的。
範例:SEMATECH CIM Framework#
在晶片製造工廠中,矽晶圓批次(lots)需要經過數百道加工步驟。製造執行系統(MES)軟體需要追蹤每個批次、記錄精確的加工過程、並指引工人或自動設備進行下一步。
產業聯盟 SEMATECH 開發了 CIM Framework:
- 定義抽象介面:為半導體 MES 領域的基本概念定義介面(即 ABSTRACT CORE 形式的 CORE DOMAIN),包含行為和語義
- 定義互動規則:任何基於 CIM Framework 的應用程式必須實作一個協定,嚴格遵守抽象介面

Figure 16.24: A highly simplified subset of the CIM interfaces, with sample implementations

Figure 16.25: The user places a lot in the next machine and logs the move into the computer.
AIDS Memorial Quilt 是 Pluggable Component Framework 的生動類比:幾條簡單規則(面板尺寸 3x6 英尺、包含紀念者姓名、耐用材料等)提供了大規模結構,讓數千人能獨立工作,同時確保整合後的一致性。
結構的限制程度#
本章討論的模式從非常寬鬆的 System Metaphor 到限制性很強的 Pluggable Component Framework。在選擇結構的限制程度時需要權衡:
- 更具限制性的結構增加一致性,使設計更容易解讀,推動開發者朝好的設計前進
- 但過多限制可能剝奪開發者需要的靈活性,特別是在跨 BOUNDED CONTEXT、異質系統的情況下
必須抵抗建構框架和嚴格規範大規模結構實作的誘惑。大規模結構最重要的貢獻是概念連貫性以及對領域的洞察。每條結構性規則都應該讓開發變得更容易。
Refactoring Toward a Fitting Structure#
Large-Scale Structure 不是瀑布式架構的回歸。唯一能找到有用結構的方式是對領域和問題的深入理解,而通往這種理解的實際途徑是迭代開發。
Minimalism(最小主義)#
- 保持結構簡單輕量,不要試圖全面覆蓋
- 早期可選擇寬鬆的結構(如 System Metaphor 或幾個 Responsibility Layers)
- 即使最小、最寬鬆的結構也能提供防止混亂的輕量指引
Communication and Self-Discipline(溝通與自律)#
- 整個團隊必須在新開發和重構中遵循結構
- 結構的術語和關係必須進入 UBIQUITOUS LANGUAGE
- 結構與程式碼細節的關係通常不在程式碼中明確表達,功能測試也不依賴結構
- 因此需要所有人持續不懈地操練這個語言
Restructuring Yields Supple Design(重構帶來柔韌設計)#
Evans 觀察到,有 Large-Scale Structure 的設計通常比沒有的更容易轉換,即使是從一種結構改變到另一種(如從 Metaphor 改為 Layers)。他用皮夾克做類比:
- 新皮夾克僵硬不舒適
- 穿過幾次後肘部開始容易彎曲,肩膀逐漸鬆開
- 數月後皮革變得柔韌舒適
經過多次以正確方式轉換的模型也是如此:不斷增加的知識被嵌入其中,主要的變化軸線已被識別並變得靈活,穩定的方面則被簡化。底層領域更廣泛的 Conceptual Contours 正在模型結構中浮現。
Distillation Lightens the Load(提煉減輕負擔)#
- 透過從 CORE DOMAIN 中移除機制、GENERIC SUBDOMAIN 和其他支援結構,減少需要重構的部分
- 支援性元素應盡可能以簡單的方式融入大規模結構(如在 Responsibility Layers 中落入單一層)
- 提煉和 refactoring toward deeper insight 的原則同樣適用於大規模結構本身——層次最初可能基於表面理解選擇,之後逐漸被表達系統基本責任的更深層抽象所取代