---
# System prepended metadata

title: DDD (~~Deadline Driven Developement~~) (Domain Driven Design)

---

# DDD (~~Deadline Driven Developement~~) (Domain Driven Design)

專注為領域知識設計系統的設計方式

[TOC]

## 名詞定義

### Domain (領域)
- 跟開發的系統相關的知識或使用情境
- 例如：台新 Richart App 開發的 domain 有，戶頭、存款、利息……
- 我們的系統 domain 有，期數、投注、打碼量、派彩……

### Model (模型)
- 用抽象化的方式解釋系統，並解決與領域相關的問題
- 例如：台新的 Richart App 可能會有：Account Model
- 我們的系統有，Draw Model
- 書基本上都用簡化的 UML 來呈現 Model，但其實 Model 的呈現方式也不一定要 UML，良好的程式碼跟 UML 有同樣的功效（但我們的程式碼通常都不怎麼……）

### Ubiquitous Language (統一戰術語言)
- 聽起來好像很厲害，其實簡單來說就是「開發者與非開發者（可能是 PM、可能是業務）」對同一件事，要有同樣的名詞稱呼。
- 好的範例：
    - Celia: 「期數」的規則是，新增的期數開始時間要晚於最後一期的期數結束時間
    - 大家都知道「期數」的定義，在同一個認知下討論事情
- 不好的範例：
    - Celia: 這邊的報表，要照「遊戲的六大分類」去呈現
    - Developer: 六大分類是？
    - 「遊戲的六大分類」=> GameCategory，我們對 GameCategory 沒有明確的名字， 所以每次講到這裡的時候都要反覆確認很多次

## 系統的架構

### Layered Architecture (多層架構)
- 就是跟 OSI 概念相近的東西 ( ﾟ∀ﾟ)
- 書中分成四個層
    - 使用者介面層 => ex: GameController
    - 應用層 => ex: GameService
    - 領域層 => 商業邏輯
    - 基礎設施層 => 封裝好資料庫操作的框架
- function 的依賴關係是單向的，由上而下，上層可以操作下層的介面，但下層不能操作上層的介面，除非使用 callback 或觀察者模式

### Smart UI
- 跟 Layered Architecture 完全相反的東西
- 大概就是把所有東西都寫在一起，方便快速暴力
![](https://i.imgur.com/ImHV40N.png)
- 書中表示， Smart UI 也是可以考慮的設計方式，工程師應該要自己分析專案所需，選擇要使用 Layered Architecture 還是 Smart UI
- 可以用 Smart UI 的專案特性：
    - 需要短時間完成的專案
    - 專案元件不需要重用
    - prototype 等級的專案，需求變化大，會需要快速迭代的專案
    - EX: 天瀚點餐系統

## 用語言建模
- DDD 重視「談話」，透過談話溝通，釐清需求，和規定統一的語言，再來設計模型
- 另外，如果連這個領域的專家，都看不懂你建的模，那你一定建錯了(σﾟ∀ﾟ)σ 
- 書中關於文件美好的想像：
    - 文件應作為程式碼與口頭交流的補充，程式碼本身能易讀到，大家都懂
    - 文件不該重複「程式碼已經明確表達出來的東西」
    - 如果文件過時了，並且沒有人覺得有必要去更新，就應該歸檔起來，以免造成混淆

## Model Driven Design
- 原則：
    - 每個模型會對應到一個領域知識，例如：Draw 對應到「期數」（如果你的模型不能對到某個規格或需求，那他可能不需要存在）
    - 程式碼用來表達模型
    - 程式碼如果修改，代表模型的邏輯被修改，背後必然牽涉到規格的更動（或是你的程式碼有很多蟲，不符合規格，也要修改）
- Hand-On Modelers
    - 他想表達：設計師關在象牙塔裡設計出來的模型，幾乎都是不合現實與規格的，所以要多跟工程師溝通設計模型

### Entity (Reference Object)
- Entity 的屬性都是具有標示性，可以「辨別」這個實體
- 我們關心 Entity 是「誰」
- 舉例：Customer （客戶）Entity 的重構
![](https://i.imgur.com/TmiRGeX.jpg)


### Value Object
- Value Object 的屬性通常是對事物的描述
- 會作為 Entity 的輔助描述
- 我們關心 Value Object 是「什麼」
- 例如：
![](https://i.imgur.com/hSNQ2XR.jpg)
- 優點
    - 節省資料庫空間
    - 共享的 Object 不可變的時候（例如：學校的教室，通常是不可變得，做選課系統的時候， Room 就可以是 Value Object）

### Service
- 上面的兩個建模都是「有狀態的」
- 而 Service 則比較像是「一系列程序性的操作」（也就是商業邏輯的部份）
- Service 由很多 interface 構成，而 interface 的行為，則由 Entity、Value Object 與規格需求，定義
- Service 的操作都是「無狀態的」（幾乎都是 pure function）

### Module
- 毫無反應，就是 Module
- Module 之間要低耦合，Module 內部要高內聚（誰不了解這段話的，那個程式設計課重修喔owo）

## 物件的生命周期，與封裝 Model

### Aggregate (骨架)
- 其實就是與某個 Entity 有關的 Entity 或 Value Object 們
- 用於在處理 Entity 的修改刪除時，要關注與其相關的其他資料一致性
![](https://i.imgur.com/qB3CMhQ.jpg)

### Factory (工廠模式)
- 毫無反應，就是工廠模式
- 工廠模式可以減少暴露過多的 Model 細節，並且讓複雜的 Model 更便於建立與使用
- 工廠模式關心的是「建立物件」，也就是物件生命周期的開始
![](https://i.imgur.com/vLhME8F.jpg)

### Repository (嗯？ Repo？)
- 將資料庫的操作封裝起來，作為 client 端要求物件模型的中間層，更便於 client 存取資料
- 他將商業邏輯的部份，與資料的存取邏輯解耦
- 他可以實踐處理， Aggregate 提到處理 Entity 修改刪除時，關注與其相關資料的一致性
- 他提供了資料存取時的邏輯
- Repository 關心的是「存取與操作物件」，也就是物件生命周期的中後段到結束
![](https://i.imgur.com/SoGaKym.jpg)

### Factory 與 Repository 並用的方式
![](https://i.imgur.com/WSmLvCc.jpg)

![](https://i.imgur.com/iLnH35l.jpg)


### Facade (外觀模式)
- 毫無反應，就是外觀模式
- 用 Facade (外觀模式) 將 Service 封裝起來，如果 Service 的介面十分複雜，可以將這些複雜的介面操作封裝起來，開發上會更加便利
![](https://i.imgur.com/Um1Gt56.png)

## 模組設計

(OS: 這個章節超難寫的，要不就簡單到我不知道怎麼下筆，要不就抽象到只可意會，不可言傳)

### Intention revealing interface
- 前言
    - 如果開發者使用 function 的時候，必須要去研究他的實作，那就失去封裝的意義了
    - 如果開發者還必須根據實作細節來推測用途，那這個 function 還可能被誤用
- 結論
    - 名字要取好啊owo，不要亂取
- 原則
    - 變數名字寫完整
    - function 要描述他的效果與目的（關心他「做什麼」），而不是他用什麼方式達到目的（而不是關心他「如何做」）
    - ex: 
![](https://i.imgur.com/AFF4QkP.jpg)

### Side effect free function
- 我覺得他想強調「把重要的商業邏輯（領域邏輯）做成 Pure Function」將狀態與與運算分開來，減少副作用
![](https://i.imgur.com/riP91TW.jpg)

### Assertion
- 在寫單元測試的時候，把隱藏起來的定義，表現出來
- ex:
    - 跑跑腿，騎士接單的時候，地點超過兩公里不能疊單（有沒有這個規定我不知道owo，隨便舉例）
    - 寫個單元測是暴露這個定義

### Conceptual contour
- 大概是從很多次的重構中，漸漸可以規劃一些底層的領域核心知識（商業邏輯/~~或稱 PM 可能想要什麼~~），當有新需求的時候，可以更快速的應變

### Standalone class
- 原則：減少依賴

### Closure of operation
- 定義：操作的時候，是操作自己的類別
- ex: function (a: Apple, b: Apple) => Apple
- 目的：減少依賴

## 系統設計

### Context

- Bounded Context：一個系統會有很多子系統，每個子系統都會有單一負責的事，界定清楚子系統要做的事，避免誤用子系統
- Continuous integration
    - 背景
        - 當很多開發者在同一個子系統下工作
    - 問題
        - A 開發者沒有了解 B 開發者的 function 到底是幹嘛的，就修改他，導致他失去作用
        - A、B 都開發了重複的 function，因為他們不知道已經有現成的 function 可以用
    - 結論
        - 當子系統也越來越大的時候，就需要分裂成更小的系統
- Context Map：兩個子系統之間的關係地圖

### Shared kernel
- 將多個系統都用到的基礎 function 抽成共用的函式庫
- 並且規定：
    - 在沒有跟，用到這個函式庫的團隊協商之前，不可以擅自修改
    - 每次整合函式庫或修改的時候，都要執行一次測試，每個用到的團隊都要執行並通過測試

### Customer/Supplier development team
- A 套件（下游）的開發，相依於 B 套件（上游）需要先開發好
- 更明顯的例子是：前後端的關係，後端的 API 是上游，而前端是下游
- 重點是上游必須要有自動化測試，確保修改程式的時候，不會改壞東西，影響下游

### Conformist
- 背景
    - 上游開發完後，倒閉或跑路了
    - 總之可能因為各種原因，所以上游不在了，新的修改沒有人做
    - 但上游的程式碼品質沒有太差
- 做法
    - 因為下游程式碼很大一部份受限於上游，所以一個辦法是順著上游的程式碼自己再做修改
    - 或者是寫一層轉換層銜接

### Anticorruption layer
- 背景
    - 像剛剛說的，上游跑了
    - 或是跟一個陳舊的系統銜接
- 作法
    - 與新系統銜接的地方，用 service 包起來，有幾個地方要接，就包幾個 service，每一個 service 都對應一個 adapter，用來轉換資料
    - 與舊系統或其他系統銜接的地方，用 Facade 包起來，只對外開出一個介面
![](https://i.imgur.com/KwoCN6k.jpg)


### Separate way
- 背景
    - 整合的代價很高，而獲益卻很小的時候
    - 需要快速完成一個專案（Side project）的時候
- 作法
    - 就...不要整合（我不知道這個章節寫出來做什麼的，混字數嗎）
    - 減少跟其他系統共用的資料，將 side project 分開設計

### Open host service
- 背景
    - 有一個很公用的子系統，會跟很多模組銜接
    - 每個模組如果都要寫一個轉換層，會有很多重複的工
- 作法
    - 定義一個 protocol，將子系統作為一組 service，有新需求的時候，就擴增這個 protocol

### Published language
- 就是資料傳輸或轉換時，一個公用的資料型態
- ex: XML、JSON

## 開發方向與重構

### Core domain
- 區分系統中，最核心、最重要的模組
    - ex: 彩票模組
- 將核心模組與其他輔助用模組區分開來
- 將重心投入在核心模組中，重構或最佳化它，並投入最好的人力進去
- 如果你的系統的核心模組並沒有特殊需求，那可以考慮乾脆直接買現成的系統

### Domain vision statement
- 願景說明，就是 Core domain 的核心價值是什麼，通常用一頁紙寫完就好，與核心價值無關的東西就不用寫了
- 他是一個指標，可以讓團隊時時刻刻寶，在對的方向

### Highlighted core
- 有了願景之後，我們可以拿願景與設計好的系統去做比較，寫成一份低於十頁的文件
- 雖然文件會有風險：
    - 沒有人維護
    - 沒有人看
    - 文件太雜
- 書中是鼓勵大家要儘量精練文件，並且從精練文件的過程中，更清楚 Core domain 與 Domain vision statement 的方向
- 而文件本身也可以作為新進人員的教材

### Generic subdomain
- 其次區分一些重要、泛用的模組，但並不是我們核心的業務
    - ex: 財務相關模組
- 將他們獨立放到單獨的 Module 中
- 他們的開發順序也比核心業務還要後面
- 而這樣的模組也可以考慮以下方式替代
    - 買現成的方案，或用開源的方案
    - 抄別人設計好的模組（他沒說哪裡找這種東西耶，可能是 GitHub 吧）
    - 外包實作模組
    - 自己做owo
- 通用不等於可重用
    - 設計通用的模組時，雖然這類型的模組裏面的程式碼或是模型可能可以被重用（resuse），但我們的目的不是要開發一個萬能的模組，所以我們不需要總是考慮模組的重用性，只要把模組設計成大部份狀況下通用就好

### Cohesive mechanism
- 前面有提到「Intention revealing interface」的一個重點是關心模組「做什麼」，而不是他「如何做」，而我們可以把「如何做」（也就是演算法的部份）封裝成另一個模組，就可以讓起他開發者不用多花心力研究他的實作

### Segerated core
- 如果 Core domain 變得太大，在開發上核心的部份會變得不太好看出來
- 這個時候可以透過「Highlighted core」提到的文件，邊歸納哪些是重要的功能或模型，邊重構或拆表
- 將比較不是核心的部份，拆成新的模組

### Abstract core
- 即使做完 Segerated Core 後，還是可能有，三四個 Module 彼此依賴，他們的關係從圖表來看，可能也還是很混亂
- 書中建議我們可以，把幾個大方向的東西用抽象類別作為代表，這些抽象類別放在獨立的 Module 可以表現彼此的關係，而實作細節就放在他原本的 Module 中
- 就是又多出一個用以表現關係的 Module

## 處理大型的結構

### Evolving order
- 背景
    - 沒有任何規則的系統設計會造成「無法理解整個系統架構和難以維護」
    - 但若在專案早期過度設計，訂下很多規定，又會讓專案開發變得綁手綁腳的
- 作法
    - 開發的規定必須要隨著專案一起演進
    - 所以隨著時間轉變，未來開發上的規定，可能跟過去截然不同
    - 另外我們在約束開發者的規定，儘量精簡，不要為了過於理想的理論，而施加不合適的規則

### System metaphor
- 背景
    - 軟體設計有的時候太抽象，會澀難懂
- 作法
    - 書中建議我們可以用一些比喻來描述系統

### Responsibility layer
- 系統發展一陣子，每個模組都有各自的職責，這時可以根據各個職責的相似度分層
- 這種分層方式很容易將模組區分出來，也可以分出哪種職責是上層，哪種職責是下層，下層的模組不能取用上層的模組

### Knowledge level
- 用 Type 的模型，來讓讓隱藏的概念轉變成顯示概念
![](https://i.imgur.com/IEhXypT.jpg)

### Pluggable component framework
- 從 interface 中分出一個 abstract core 並建立一個框架，讓框架透過 abstract core，可以自由替換實作介面
- 透過 pluggable component framework 可以讓系統自由與其他系統整合
- 但同時，也會限制 core domain 的發展，因為修改 abstract core 的成本更大

## 制定戰略的要點
- 決策必須傳達給整個團隊
- 決策必須收集回饋意見
- 計劃必須允許演變
- 架構團隊不必把所有最厲害的人都吸收進來
- 戰略儘量簡潔

## 結論
- 如果不是這本書翻譯的不好，就是 DDD 是一門玄學
