為什麼還需要新模型#
Chapter 6 結尾留下了一個矛盾:用反射讀取私有欄位的整合,在 module coupling 是最強的 content coupling,在 connascence 卻只是最弱的 connascence of name。這不是錯誤,而是兩個模型描述的是耦合的不同面向。
本章把兩個模型的精華整合成一個更實用的工具:整合強度(integration strength)。
本章與後續章節中的「interface」一詞,指模組的整合介面,而不是 Java/C# 中的
interface關鍵字。
耦合的兩個關鍵屬性#
跨模組互動有兩個必須同時考量的屬性:
- 介面類型(interface type):用什麼樣的介面整合?私有介面(content)?公開介面(stamp、data)?甚至完全沒有實體連線?
- 介面複雜度(interface complexity):透過介面共享的知識有多複雜?越複雜越容易產生連動修改與整合錯誤
光用 module coupling 評估會看不到細緻度;光用 connascence 評估會看不到介面類型。兩者必須一起用。
兩模型的盲點#
重複實作的隱形耦合#

Figure 7.1: 兩個服務各自實作同一條業務規則
考慮兩個服務 Retail 和 Fulfillment 各自實作同一條業務規則「客戶是否合資格享免運」。
- 兩者並沒有實體連線
- 卻有極強的「共享變更原因」——任何規則變動都得同時改兩處
- 不同步的話,會出現「下單時符合、出貨時被收費」的不一致
Module coupling 與 connascence 都無法描述這種耦合。它們只在「實體連線」存在時才適用。
這也不是 connascence of algorithm——後者要求「雙方需用同一演算法解讀資料」,而這裡的重複邏輯與資料交換無關。
Integration Strength 的目標#
新模型要達成三個目標:
- 實用(Practicality):日常工作中容易理解與套用
- 通用(Versatility):適用於從程式碼中個別語句到分散式系統間的服務
- 完整(Completeness):補足 module coupling 與 connascence 的盲點
模型由四個基本層級組成(由強到弱):
- Intrusive coupling(侵入式耦合)
- Functional coupling(功能耦合)
- Model coupling(模型耦合)
- Contract coupling(契約耦合)
四個層級反映「介面類型」;其中後三者還有一個額外維度——degree(程度)——用 connascence 評估。
貫穿範例:兩個服務共用資料庫#

Figure 7.2: 兩個服務共用同一個資料庫的陷阱問題
「兩個服務共用資料庫,到底算強耦合還是弱耦合?」——這是一個故意的陷阱問題。光看圖無法判斷,必須看更多細節。後面四個層級都會回到這個範例做對照。
Intrusive Coupling(侵入式耦合)#

Figure 7.3: 侵入式耦合的概念示意
下游模組透過上游的實作細節整合,而非透過為整合設計的公開介面。
Intrusive coupling 等同於 structured design 的 content coupling。書中改名是因為「content」原意是程式碼語句的內容,現代有更多種形式的實作細節依賴。「Intrusive」由 Vaughn Vernon 命名,更能反映「越界」的本質。
例子#
- 用反射呼叫私有方法、讀取私有欄位
- 改動商用 off-the-shelf 系統的原始碼塞自己想要的通知機制(升級時整個被覆蓋)
- 微服務直接讀另一個微服務的資料庫——若該資料庫從未被設計為整合介面
ORM 框架用反射存取物件並不算侵入式耦合——使用 ORM 時你已明確同意它存取物件欄位。差異在於「上游是否知情並允許」。
範例(共用資料庫)#

Figure 7.4: 存取非為整合而設計的資料庫導致侵入式耦合
服務 B 直接讀服務 A 的資料庫,且該資料庫從未被設計成整合介面 → 侵入式耦合。
為何位於最頂層#
- 脆弱:上游任何修改都可能破壞整合
- 隱晦:上游甚至不知道有人依賴它,連審查破壞性變更的機會都沒有
- 破壞封裝:必須假設上游所有實作細節都已對下游公開
Functional Coupling(功能耦合)#
兩個模組的功能互相關聯——透過共享業務規則、不變量、演算法等業務邏輯耦合在一起。
功能耦合沒有上下游之分:知識雙向流動,任一邊修改都可能波及對方。
三種程度#
由弱到強:
- Sequential functionality(順序功能)
- 多個動作必須以特定順序執行
- 對應 connascence of execution / connascence of timing
- Transactional functionality(交易功能)
- 多個操作必須作為單一工作單元成功或失敗,需要並發控制
- 對應 connascence of value(值需一起變動)與 connascence of identity(共享資料的強一致性)
- Symmetric functionality(對稱功能):最強的功能耦合
- 不同模組實作同一份功能
- 對應違反 DRY 原則:「Every piece of knowledge must have a single, unambiguous, authoritative representation within a system」
- 即便不同實作方式也算(重點是「同一個業務知識」)
- 因為高度隱晦(兩邊可能完全不知道對方存在)卻必須同時改,所以僅次於 intrusive coupling

Figure 7.5: 功能耦合的三種程度
識別功能耦合的捷思:「如果業務需求變了 X,會不會有多個模組都得改?」會 → 功能耦合。
範例(共用資料庫)#

Figure 7.6: 兩個服務操作同一份資料導致功能耦合
兩個服務都讀寫同一張資料表,且都需遵守同一份業務規則維持資料一致性 → 功能耦合,程度為 connascence of identity。
Model Coupling(模型耦合)#
多個模組共用同一個業務領域模型。
模型不是業務領域的完整重現,只是「為了實作某項功能而抽取的子集」。「All models are wrong, but some are useful.」(George Box)。
不同的業務功能往往需要不同的模型。DDD 之所以強調為不同功能設計多個模型,正是這個道理。
為何模型不該被任意共享#

Figure 7.7: 上游模組將內部模型當作公開介面暴露
範例:Distribution 模組把內部模型當公開介面對外暴露,Accounting 模組重用了它的部分模型。
Accounting可能本來需要不同的模型來表達自己的業務- 一旦模型變動,連動修改就會跨模組擴散
- 限制了
Distribution自己模型的演進能力
此層級只關心「資料模型」共享。共享行為屬於 functional coupling。
Degree#

Figure 7.8: 模型耦合的程度
用靜態 connascence 五個層級評估(name、type、meaning、algorithm、position)。
範例(共用資料庫)#

Figure 7.9: 讀取另一元件的營運資料庫導致模型耦合
服務 A 擁有資料庫並負責管理資料,但允許服務 B 唯讀。資料庫此時就是公開介面,但讀到的是服務 A 的內部模型 → 模型耦合。
為何比侵入式與功能耦合好#
- 暴露的是資料而非行為——資料結構通常比行為穩定
- 整合介面顯式——知道哪些模型被共享,可記錄、可識別破壞性變更
- 上游知情——可以採取措施避免破壞性變更
但仍可能引入意外性複雜度(暴露不必要欄位 = stamp coupling 的延伸)。
Contract Coupling(契約耦合)#

Figure 7.10: 透過為整合最佳化的模型連接下游元件
模組之間透過整合專用模型(integration-specific model,即「contract」) 通訊。
Contract 是「模型的模型」——把業務領域模型中與整合無關的部分抽掉,暴露出最小必要知識。
引入整合契約的好處#
- 比實作模型更穩定:實作可以演進,只要還能對應到同一份契約,就不會波及下游
- 最小化共享知識:契約以外的內部模型完全封裝
- 可版本化:同一上游模組可同時暴露多個版本契約,方便下游漸進遷移
範例:把領域物件抽成 DTO#
把內部 SupportCase(含 CaseId、AgentId、CustomerId 等領域物件)轉成只用 primitive 型別的 SupportCaseDetails,方便跨平台。
範例:用 Command / Query 構造任務導向介面#
namespace WolfDesk.SupportCases.Application.API.Commands {
class EscalateCase(val caseId: String, val customerId: String, val escalationReason: String)
class ResolveCase(val caseId: String, val comment: String)
class PutCaseOnHold(val caseId: String, val comment: String, val until: Long)
}
interface SupportCasesAPI {
fun execute(cmd: EscalateCase): ExecutionResult
fun execute(cmd: ResolveCase): ExecutionResult
fun execute(cmd: PutCaseOnHold): ExecutionResult
fun casesByAgent(agentId: String): List<SupportCaseDetails>
fun casesByCustomer(customerId: String): List<SupportCaseDetails>
fun escalatedCases(): List<SupportCaseDetails>
}Contract coupling 不只在分散式系統。Facade、Bridge、Adapter 等設計模式本質上都是某種形式的整合契約。SMTP/IMAP/POP3 也是現成的整合契約。
Degree#

Figure 7.11: 契約耦合與模型耦合的程度對照
跟 model coupling 一樣,用靜態 connascence 評估。
即便 contract coupling 中最強的程度,也比 model coupling 中最弱的程度更弱。差別在於「共享的知識類型」:模型是實作細節,契約不是。
Contract 的「深度」#

Figure 7.12: 評估模組的視覺啟發 — 深度
// Operational model
class Message(val id: UUID, val body: String, val sentOn: DateTime, val sentBy: UUID)
// "Contract",但欄位完全相同 → 沒封裝任何東西
class MessageDTO(val id: UUID, val body: String, val sentOn: DateTime, val sentBy: UUID)這種 DTO 是個「淺模組」——沒封裝任何知識,反而增加了不必要的「moving part」。雖然能讓
Message與MessageDTO各自演進,但鏡像結構暗示變動仍會跟著傳導。寧可先用 model coupling,等真正有封裝價值時再引入契約物件。
範例(共用資料庫)#

Figure 7.13: 透過暴露含整合專用資料的資料庫 schema 形成契約耦合
資料庫中有一張專為整合設計的表,schema 與服務 A 的實作模型解耦 → contract coupling。
軟體設計中常用「semantic coupling」來涵蓋 model coupling 與 contract coupling,但本書刻意分開——兩者整合強度差距太大,混為一談會抹殺重要差異。
Integration Strength 與兩前模型的對照#

Figure 7.14: 共享變更原因隨介面類型與複雜度而變化
| Integration Strength | Module Coupling | 用途 |
|---|---|---|
| Intrusive | Content | 上游沒打算被當整合介面 |
| Functional | Common / External / Control | 行為層面的依賴 |
| Model | Stamp | 共享了多餘資料結構 |
| Contract | Data | 介面為整合而設計,最小化共享 |
整合強度 = 介面類型(四個基本層級) × 介面複雜度(degree 用 connascence 衡量)
範例:分散式系統#

Figure 7.15: 分散式系統的範例
考慮一個分散式系統:
- 服務 A 接受使用者命令 → 寫入資料庫 + 發訊息到 message bus
- 服務 B:把訊息存進自己的資料庫供稽核
- 服務 C:用訊息更新自身狀態
- 服務 D:用服務 C 提供的狀態 enrich 訊息後,送進分析資料庫;訊息延遲 30 秒投遞,等服務 C 處理完
可以辨識出:
- 資料庫 + message bus 必須交易性更新 → functional coupling,degree = connascence of value
- 服務 C 用了服務 A 公開的領域模型 → model coupling
- 服務 D 必須等服務 C 處理完才能跑(30 秒延遲)→ functional coupling,degree = connascence of timing
同步 vs 非同步#

Figure 7.16: 同步與非同步整合的元件
業界常認為非同步比同步耦合弱。本章直接挑戰這個觀念。
同步或非同步通訊本身並不影響整合強度。 它影響的是其他面向,將在 Chapter 8(距離)中討論。
例如同樣用 message queue 通訊,可能對應到:
- 訊息是整合專用模型 → contract coupling
- 訊息直接是業務領域模型 → model coupling
- 訊息有延遲投遞語意 → functional coupling(connascence of timing)
- 兩端必須同時成功或同時失敗(含補償動作)→ functional coupling(connascence of value)
- 訊息匯流排其實是上游的內部實作 → intrusive coupling
重點整理#
評估某個整合時,問自己兩件事:
- 我用的是哪一種介面?私有實作?業務功能耦合?業務模型?整合契約?
- 透過這個介面共享的知識複雜度有多高?(用 connascence 評估)
下一章把焦點從「強度」轉到另一維度:距離(distance)。