---
# System prepended metadata

title: Ch.01 Introducing Domain-Driven Design 筆記

---

# Ch.01 Introducing Domain-Driven Design 筆記

## Overview

將以 DDD 手法探討如何分析現實世界的 `訂單系統` 。

## Garbage in Garbage out

有人認為 developer 只是寫 code。

事實上 developer 是以 software 解決問題，coding 只是其中一部分，尚包含 Communition  與 Good Design，這也是本書要強調的部份。

假如你也承認 software development 是一種 pipeline，以 `需求` 為 input，以 `解決問題` 為 output，則 `garbage in, garbage out` 法則也適用，如果 input 為 `不明確需求` 或 `不好的設計`，無論再好的 code 而無法產生預期 output。

本書 part 1：`Understanding the Domain` 就是為了減少 `garbage in`，以 DDD 為方法更清楚的 Communication 與 Good Design (Shared Domain Knowledge)。

本章節將以實際範例討論 DDD 原則並實際分析 domain。

DDD 並不是萬靈丹，也不適用於如 system software、games ... 等領域，特別適合 business 或 enterprise software，也就是 developer 必須與其他非 IT 領域的團隊一起合作，這是本書所要討論的。

## The Importance of a Shared Domain Model

![](https://i.imgur.com/i9MXOWB.png)


在解決問題之前必須先了解問題是什麼，但可惜的是 Development Team 認知與 Domain Expert 認知不一定相同，因此可能產生 `garbage in`。

一些 software development process 使用 specification 或 requirement document 嘗試以 Development Team 角度捕捉所有問題，由小孩子玩的 `傳聲筒` 遊戲經驗可得知，訊息經過一連串人的理解與傳遞之後，訊息常常會不斷變形，因此造就彼此認知落差。

> * Developer Team：Developer、UX/UI designer、Tester
> * Domain Expert：真正了解 Domain 的專家

![](https://i.imgur.com/XXOXTew.png)


Agile 採用方式是 Domain 與 Development Team 直接溝通，避免中繼人員的理解與傳遞，如此可有效避免 `訊息變形`。

但這樣仍有些缺點：

* Developer Team 必須將 Domain expert 心中的 Mental Model 翻譯成 Developer 所理解的 Domain Model 用於 code 中，這仍可能造成 `訊息變形` 或忽略重要細節
* 由於 Developer 的 Domain Model 與 Domain Expert 的 Mental Model 仍有所差異，將來接手維護的 Developer 由於沒有直接與 Domain Expert 溝通過，只看到目前 code 中的 Domain Model，因此可能對真正 Domain 造成誤解

![](https://i.imgur.com/T6fJjT1.png)


DDD 則希望 Domain Expert、Development Team 、other stakeholder 都使用相同的 Mental Model 為 Domain Model，code 只是用來處理此 Domain Model 而已，而非 Developer 根據自己的理解重新建立 Domain Model。

使用共同的 Domain Model 有幾個優點：

* **更短的 time to market**：避免 `訊息變形` 造成誤解而能更快速解決問題
* **更多商業價值**：避免只是做出 developer 想要的產品而非 customer 想要的產品而走偏
* **更少浪費**：更清楚的需求避免 `garbage in` 可減少將來 rework 時間，也更能儘早發現哪些才是 Domain Expert 認為重要的部分，讓 Development Team 將資源放在對的地方
* **更易維護**：由於 code 中的 Domain Model 與 Domain Expert 一致，也讓將來接手維護的 developer 更容易與 Domain 結合上手

> **The Insanely Effective Delivery Machine**
>
> Dan North 是一個著名的 Behavior-Driven Development 傳教士，他曾在 `Accelerating Agile` 分享在幾周內開快速開發出 trading system 經驗，他認爲成功關鍵在於 Shared Mental Model，它讓每個 developer 直接與 Domain Expert 溝通，並使用相同的 Domain Model，這使的彼此溝通非常有效率，且最後 developer 也成為 Domain Expert，並能快速建立系統

DDD 歸納出幾個 guideline 協助我們建立 shared domain model：

* `將焦點放在 Business Event 與 Workflow 而非 Data Structure`
* `將 Problem Domain 切割成更小的 Subdomain`
* `根據 Subdomain 建立 Domain Model`
* `為 Developer Team 與 Domain Expert 間建立 Ubiquitous Language`

## Understanding the Domain Through Business Events

根據 DDD 第一條 guideline：

> 將焦點放在 Business Event 與 Workflow 而非 Data Structure

企業並不是只有 data，而是在於將 data 經過處理而獲利，這是一個 transformation。

到底是什麼促使 transformation 開始呢 ? 它常常是外部 trigger：

* 有 email 進來
* 有電話進來
* 每天 10：00 要做 
* 若沒有任何訂單就做其他事情

一開始必須將這些 Domain Event 都抓出來。Domain Event 是建立 Domain Model 的起手式。

> 如 `當收到訂單時` 就是一個 `訂單系統` 中觸發 `處理訂單` process 的 Domain Event。

Domain Events 會以 `英文過去式` 表示，表示已經發生且無法改變的事實。

### Using Event Storming to Discover the Domain

* 將所有相關人都加入討論 (Domain Expert、Development Team、other stakeholder)
* 牆壁必須有 `白報紙`  或 `白板` 可貼 `便利貼` 及直接書寫
* 所有人將 Business Event 與其觸發的 Business Workflow 寫在便利貼並貼在牆
* 依 `時間軸` 先後調整便利貼順序
* 目的將所有的 Business Event 與 Business Workflow 都找出來

### Discovering the Domain: An Order-Taking System

本書將以 `訂單系統` 為實例探討 DDD、Domain Model 與 FP 實作。

Max (總經理)：

> 我們是個小公司，目前訂單作業都是人工與書面處理，由於公司業務擴展迅速，目前訂單處理已經非人工所能負荷，我們希望能全面數位化處理，因此希望有個 seft-service 網站，顧客可自行處理某些訂單業務，如 `下訂單`，`查詢訂單狀態` ... 等

You：

> 請開始用便利貼將 Business Event 貼在牆上：

Ollie (訂單部)：

> 我是 `訂單部` 的 Ollie，我們會處理 `訂單` 與 `報價單`

You：

> 什麼事件會引發這個流程呢 ?

Ollie (訂單部)：

> 當顧客藉由 email 將 `表格` 傳給我們時

You：

> 聽起來像是 `order form received` event 與 `quote form received` event 是嗎 ?

Ollie (訂單部)：

> 是的，讓我們將這些 event 用便利貼貼在牆上

Sam (物流部)：

> 我是 `物流部` 的 Sam，當 `訂單` 成立時，我們將 `接單出貨`

You：

> 你何時會知道該 `接單出貨` 呢 ?

Sam (物流部)：

> 當我們收到 `訂單部` 的表格時

You：

> 你會如何稱呼該 event 呢 ?

Sam (物流部)：

> 稱為 `order avaliable` 如何 ?

Ollie (訂單部)：

> 我們稱即將出貨的訂單為 `placed order`，我們可對該名詞統一嗎 ?

Sam (物流部)：

> 所以稱為 `order placed` event 大家同意吧 ?

![](https://i.imgur.com/jH0Z5dJ.png)


經過一段時間討論，牆上出現以上便利貼。

由以上對話，我們可發現藉由 Event Storm 可幫助我們釐清以下幾件事情：

* **凝聚各部門共識**

除了探索出 Business Event 外，因為參與者都一起看到牆上的便利貼，使得大家對整個系統凝聚共識，讓每個部門了解其他部門的 Domain，也澄清了對其他部分 Domain 的誤解，甚至 Development Team 也可發現藉由 IT 技術對整個 Business 有新的解決問題方法。

* **增進各部門了解**

多數人容易以自己的部門來思考事情，而忘記了其他部門也可能相關，

Black (帳務部)：

> 我是 `帳務部` 的 Blake，我們也需要完整的訂單資料，藉此我們才能對顧客收費，因此我們也需要參與 `order placed` event

* **找出未發現需求**

當所有 Business Event 都根據時間軸攤在牆上時，很容找出未發現需求。

Max (總經理)：

> Ollie (訂單部)，當訂單成立時，你有告知顧客嗎 ? 我在牆上沒看到便利貼

Ollie (訂單部)：

> 是的，我忘記了，當訂單成立時，我們會發 email 給顧客通知訂單已經成立並準備出貨，這是另一個 event：`order acknowledgement sent to customer` event

假如問題無法馬上有明確的答案，應該將 `問題本身` 以便利貼貼到牆上，將來另外討論。

事實上很多需求一開始都很模糊，與其一開使就貿然進行開發而事後一再 rework，不如先把問題紀錄下來，另外找時間將問題釐清。

* **各部門以 Event 結合**

![](https://i.imgur.com/X7oh9vt.png)


當所有 Event 與 Workflow 都貼在牆上時，可進一步將之以 `部門分類`，如此可發現當 `訂單部` 訂單成立時，其 output 發起 `order placed` event 正是 `物流部` 與 `帳務部` 的 input。

在此階段並不討論實作細節，如該使用 message quene 或 database ... 等，這裡只在乎 Domain 中各部門的關係。

* **察覺出報表需求**

在了解 Domain 時很容易發現 rocess 與 transaction，但別忘了 reporting 也一樣重要，Event Storm 階段也可一起討論出 reporting 與 UI 部分。

### Expanding the Events to the Edges

隨著 Event Storm 挖掘出更多的 Business Event 且依時間軸排序後，你應該不斷問自己是否最左側與最右側還存在其他 Event，試圖找出 system 的 boundary。

You：

> Ollie (訂單部)，什麼會引起 `order form received` event 呢 ?

Ollie (訂單部)：

> 每天早上我們會開啟 email 查看是否有新的 `訂單` 或 `報價單`

You：

> 所以聽起來我們還需要 `mail received` event 嗎 ?

同理我們也可找出 system 最右側的 boundary。

You：

> Sam (物流部)，是否存在任何 event 在 `Order shipped` event 之後呢 ?

Sam (物流部)：

> 如果訂單是 `貨到簽收`，當顧客收到貨後，我們會從 `客服部` 收到通知，所以應該加上 `shipment received by customer` event

![](https://i.imgur.com/VtrLlpW.png)


* **Scenario**：User 或 department 想要達成的目標，但不包含實際細節，Agile 稱為  **story**。

* **Use Case**：Scenario 的實際細節。

* **Busines Process**：企業想要達成的目標。

* **Workflow**：Business Process 的實際細節。

### Documenting Commands

當使用 Event Storm 在牆上找到眾多 Business Event 之後，下一步的疑問就是：如何使這些 Event 發生呢 ?

如 `顧客發 email` 的動作才會使 `order form received` event 發生，在 DDD 稱為 **Command**。

>不要跟 OOP 的 Command Pattern 搞混

當 Command 成功執行，它會引起 Workflow，並且發起 Business Event。

* `Make X happen` Command：

執行 `make X happen` workflow，並發起 `X happend` event。

* `Send an Order form to Widget Inc.`

執行 `send the order` workflow，並發起 `Order form sent` event。

* `Place an order` command：

執行 `place an order` workflow，並發起 `Order placed` event

* `Send a shipment to customer ABC` command：

執行 `send a shipment` workflow，並發起 `Shipment send` event

![](https://i.imgur.com/3K1XjEE.png)


實務上 Business Process 會以如上的 Model 呈現：

* 由 Event 觸發 Command
* 由 Command 執行 Workflow
* 由 Workflow 觸發更多 Event

> 我們可發現這種 Model 剛好跟 FP 的 Function Pipeline 相契合。

![](https://i.imgur.com/FjwnUnD.png)


根據以上方法呈現我們的 `訂單系統`。

目前我們先假設所有 Command 都成功執行，將在 `Ch.10 Implementation: Working with Errors` 討論如何處理 Command 執行失敗。

此外，並非所有 Event 都是由 Command 觸發，有些 Event 可能由 scheduler 或 monitor system 觸發。

## Partioning the Domain into Subdomains

根據 DDD 第二條 guideline：

> 將 Problem Domain 切割成更小的 Subdomain

雖然目前已經搜集了眾多 Event 、 Command 與 Business Process，但對於整個系統的了解仍是混亂的，在寫 code 之前仍必須做更進一步整理。

也就是將眾多 Event、Command 與 Business Process 的 `訂單系統` 切割成更小的子系統：如 `訂單子系統`、`物流子系統`、`帳務子系統` ... 等，誠如企業中已經存在各部門一樣，我們可稱此子系統為 Domain。

Domain 在 DDD 中定義為 `有特定知識的領域`，白話文就是 `存在 Domain Expert 的領域`。

![](https://i.imgur.com/5RhXl7K.png)


以我們都熟習的 Domain 而言，`Web Programming` 算 `General Programming` 的 Subdomain，而 `JavaScript Programming` 又是 `Web Programming` 的 Subdomain。

除此之外，Domain 也能 overlap，如 CSS 橫跨 `Web Programming` 與 `Web Design`。

理論上，我們都希望切出來的 Subdomain 能明確而互不相干，但現實世界並沒有想像中單純。

![](https://i.imgur.com/5f4ligi.png)


現實中的 Domain 會有些 overlap，`訂單部` 必須稍微了解 `物流部` 與 `帳務部` 流程，而 `物流部` 也對 `訂單部` 與 `帳務部` 的業務有所認識。

誠如之前 Dan North 的例子，developer 也該成為半個 Domain Expert，整個開發效率才會大幅提升。

## Creating a Solution Using Bounded Contexts

根據 DDD 第三條 guideline：

> 根據 Subdomain 建立 Domain Model

了解 Problem 與 Domain 並不代表能建立 Solution，畢竟資訊太多了，我們必須找出能解決問題的 Event、Command 與 Process。

![](https://i.imgur.com/MHPv2lG.png)


首先我們必須將 Problem Space 與 Solution Space 分開，在 Problem Space 中從眾多 Event、Command、Process 中取出與解決問題的資訊在 Solution Space 中建立 Domain Model。

在 Solution Space 中的 Domain 與 Subdomain 在 DDD 稱為 **Bounded Context**，在每個 Bounded Context 中有其各自 Domain Model。

> 之所以 DDD 不稱為 Subsystem，因為其重點在於其 Context 與 Boundary

### Context

在相同 Context 中有其專有名詞與行話，這使的參與其中的人使用共同語言溝通，因為在現實世界中，同一個單字在不同領域常有不同意義。

### Boundary

在現實世界中，Domain 很難無 overlap，但在 software 開發上，我們會希望各系統能避免彼此耦合、沒有 side effect 而能獨立演進，為了是減少複雜度與方便日後維護，因此我們傾向建立彼此 `沒有 overlap` 的 Domain Model，僅管此 Model 與現實世界的 Domain 有些許出入。

> Bounded Context 與 Subdomain 還是會有些差異，主要是為了更適合 software 開發

在 Problem Space 的 Domain 與 Solution Space 的 Bounded Context 並不需要一對一對應，實務上常常一個 Domain 分裂成多個 Bounded Context，或者多個 Domain 合併成單一 Bounded Context。

假如公司已經分別有 `訂單系統` 與 `財務系統`，假如你必須整合這兩個系統，僅管現實上是不同 Domain，但設計上會使用相同 Bounded Context。

無論怎麼切割 Domain，重點是每個 Bounded Context 有其明確的 responsibility，因為在實作 Domain Model 時，每個 Bounded Context 實作方式可能是獨立 DLL、可能是獨立 Service，或者是獨立 namespace，雖然實作方式並不是現階段重點，但大方向是相同的。

### Getting Context Right

定義 Bounded Context 聽起來很直覺，但實作上卻很 tricky，事實上 DDD 最大的挑戰就是 `如何決定正確 boundary ?`，這已經是藝術等級，而非科學問題，以下是 DDD 所建議的 guideline：

* `傾聽 Domain Expert 建議`：假如該 Event、Command 與 Process 都使用相同行話且關注相同問題， 他們大致上是在相同 Bounded Context 內
* `觀察企業的組織劃分`：畢竟因為業務不同才會畫分出如此的組織結構，他們大致上是在相同 Bounded Context 內
* `別對 Boundary 有婦人之仁`：由於問題變化太快，可能會想將 boundary 模糊化面對，但 boundary 太大或太模糊等於沒有 boundary，俗語說：`Good fences make good neighbors`。
* `為了將來獨立發展`：硬將不相關的 Domain 取其最大公約數放在相同 Bound Context 下將限制未來發展，應將其各自放在不同 Bounded Context 下
* `為了更順暢的 Workflow`：假如該 Workflow 會橫跨多個 Bounded Context，但因為 Cotext 的種種限制而使 Workflow 不順，可考慮將多個 Bounded Context 合而為一，雖然整個設計可能看起來比較醜，但畢竟 DDD 重視的是解決問題與客戶價值，而非設計出漂亮的架構

沒有任何架構是一成不變的，任何 Domain Model 都會隨時間的推進與需求的改變而演化，在 `Ch.13 Evolving a Design and Keep it Clean` 將會深入討論。

### Creating Context Maps

當設計出 Bounded Context 後，我們還必須設計出各 Bounded Context 溝通方式，此時並不需要對實作部分太講究，只需有整體概念即可，在 DDD 稱為 **Context Map**。

![](https://i.imgur.com/Y5a3uU5.png)
![11](images/11.png)

如同旅行時使用的 route map 一樣，它並沒有顯示各城市細節，也沒有顯示以何種交通工具來往，它只顯示出下一站的目的地，讓你可以根據它設計出旅行計劃。

![](https://i.imgur.com/sOrd8Ny.png)


如同 route map 一樣，Context Map 主要在顯示各 Bounded Context 間的關係而非實作細節，可以大致看成 `Order-taking` context 是 upstream，而 `Shipping` context 是 downstream。

很明顯的當兩個 Bounded Context 要溝通時，必須同時遵守一定的通訊協定，通常 upstream 要較高的影響力決定，但有時候因為 downstream 是 legacy system 無法輕易改變，有可能 upstream 必須妥協，或種中間有 adapter 進行轉換，詳細會在 ` Ch.03 A Functional Architecture` 中討論。

雖然目前的 `訂單系統` 只需一個 Context Map 就能搞定，但實務上會有很多 Context Map 用來表示各自 Subsystem。

### Focusing on the Most Important Bounded Context

當分割出眾多 Bounded Context 之後，要從哪個 Bounded Context 開始開發呢 ?

* **Core Domain**：具有企業核心優勢，能使企業賺錢的 Domain
* **Supportive Domain**：必須但非核心的 Domain
* **Generic Domain**：對企業並非必須的 Domain

以本例而言，`Order-taking` 與 `Shipping` 可視為 Core Domain，而 `billing` 則為 Supportive Domain。

## Creating a Ubiquitous Language

根據 DDD 第四條 guideline：

> 為 Developer Team 與 Domain Expert 間建立 Ubiquitous Language

因為 DDD 強調 Code 與 Domain Expert 必須使用相同的 Mental Model，如 Domain Expert 使用 `Order` ，在 Domain Model 裡也必須稱為 `Order`，Developer Team 不能根據自己的喜好使用其他字彙。

除此之外，Domain Model 也不該出現 Domain Expert 的 Mental Model 中所沒有字彙，如 `OrderFactory`、`OrderManager`、`OrderHelper` ... 等，當然這些字彙在 codebase 中可能會出現，但不應該在 Domain Model 出現，因為 Domain Expert 並不了解。

由於要讓所有人都能了解 Domain Model，因此必須統一大家的觀念與字彙，在 DDD 稱為 **Ubiquitous Language**，白話就是 everywhere language，也就是 Developer Team 與 Domain Expert 在 Domain Model 所使用的共同字彙，除此之外，既然是 everywhere，在 codebase 也該使用相同字彙。

不要以為 Ubiquitous Language 只是 Domain Expert 所定義而已，這是整個 team 共同制定的，且 Ubiquitous Language 也不是一成不變，而是會隨著 project 的進行而加入新的觀念或字彙，在本書後續章節也會看到。

雖然有了共識的 Ubiqutious Language，但有時同一個字彙在不同 Bounded Context 可能有不同定義，如同 `方言` 一般。

如 OOP 的 `class` 與 CSS 的 `class` 就是完全不同意義。

如 `order` 在不同組織中的意義也可能有些許差異，在 `物流部` 關心的是 `產品數量`，而 `帳務部` 則關心 `產品單價` 與 `總金額`，所以儘管有了 Ubiquitous Language 後，仍必須注意其 Bounded Context，否則可能造成誤解。

## Summarizing the Concepts of Domain-Driven Design

* **Domain**：我們要解決的問題中所使用的專業知識領域，會有 Domain Expert 存在
* **Domain Model**：在 Solution Space 內經過簡化的模型
* **Ubiquitous Language**：Domain 中所有 team member 都使用的共同字彙
* **Bounded Context**：在 Solution Space 中有明確 boundary 的 subsystem，且存在 Ubiquitous Language 的 `方言`
* **Context Map**：顯示 Bounded Context 之間關係
* **Domain Event**：系統中會發生的 Event，會以 `過去式` 表示，且會觸發其他 Command
* **Command**：執行 Process 或觸發 Event，可由其他 Event 觸發

## Wrapping Up

一開始我們強調 Shared Mental Model 的重要性，接著提出 DDD 四個 guideline：

* `將焦點放在 Business Event 與 Workflow 而非 Data Structure`
* `將 Problem Domain 切割成更小的 Subdomain`
* `根據 Subdomain 建立 Domain Model`
* `為 Developer Team 與 Domain Expert 間建立 Ubiquitous Language`

### Event and Process

![](https://i.imgur.com/OQJwxVn.png)


Event Storm 中找出了 Domain 中重要的 Event，如：

* `Order form received` event 觸發 `Place order` process
* `Place order` process 執行完觸發 `Order Placed` event
* `Order Placed` event 觸發 `Ship Order` process 與 `billing` process

### Subdomain and Bounded Context

![](https://i.imgur.com/BJci5ZS.png)


You：

> Ollie (訂單部)，你知道 `billing` process 如何運作嗎 ?

Ollie (訂單部)：

> 一點點，但詳細內容要問 `物流部` 的 Sam

由此可知 Billing 為另外一個 Domain，也就是所謂的：

> A Domain is what a Domain Expert is expert in

![](https://i.imgur.com/asiRcek.png)


接著我們可將 Problem Space 的 3 個 Subdomain 以 Solution Space 的 3 個 Bounded Context 對應。

![](https://i.imgur.com/RR3kvyi.png)


以 Context Map 顯示 Bounded Context 之間的關係。

至於何者為 Core Domain，這應該由總經理 Max 決定，我們假設 `Order-taking` Context 是 Core Domain，將由此 Bounded Context 開始開發。

### The Ubiquitous Language

目前我們已經有如 `order form`、`quote`、`order` 等專有名詞，且隨著專案的進行，還會出現更多專有名詞，為了讓團隊有共識，且讓新進成員快速上手，可建立 live document 或 Wiki 之類加以記錄這些專有名詞及其定義。

### What's Next ?

雖然目前對於 `訂單系統` 已經有不少了解，但仍缺乏一些東西：

* `Order-processing` workflow 詳細的 input 與 output 為何 ?
* `Order-processing` workflow 與哪些 Bounded Context 相關呢 ?
* `物流部` 的 `order` 與 `帳務部` 的 `order` 認知詳細差異在哪 ?

在 `Ch.02 Understanding the Domain` 將試圖討論以上問題。

## Conclusion

* 對於 DDD 完全沒概念的初學者，可藉由本章對於 DDD 手法有初步認識
* 對於 DDD 最經典專有名詞如 Event Storm、Bounded Context、Abiquitous Language ... 也能有初步認識

## Reference

[Scott Wlaschin](https://twitter.com/ScottWlaschin?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor), [Domain Modeling Made Function Preface](https://pragprog.com/book/swdddf/domain-modeling-made-functional)