# 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 完全相反的東西
- 大概就是把所有東西都寫在一起,方便快速暴力

- 書中表示, 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 的重構

### Value Object
- Value Object 的屬性通常是對事物的描述
- 會作為 Entity 的輔助描述
- 我們關心 Value Object 是「什麼」
- 例如:

- 優點
- 節省資料庫空間
- 共享的 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 的修改刪除時,要關注與其相關的其他資料一致性

### Factory (工廠模式)
- 毫無反應,就是工廠模式
- 工廠模式可以減少暴露過多的 Model 細節,並且讓複雜的 Model 更便於建立與使用
- 工廠模式關心的是「建立物件」,也就是物件生命周期的開始

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

### Factory 與 Repository 並用的方式


### Facade (外觀模式)
- 毫無反應,就是外觀模式
- 用 Facade (外觀模式) 將 Service 封裝起來,如果 Service 的介面十分複雜,可以將這些複雜的介面操作封裝起來,開發上會更加便利

## 模組設計
(OS: 這個章節超難寫的,要不就簡單到我不知道怎麼下筆,要不就抽象到只可意會,不可言傳)
### Intention revealing interface
- 前言
- 如果開發者使用 function 的時候,必須要去研究他的實作,那就失去封裝的意義了
- 如果開發者還必須根據實作細節來推測用途,那這個 function 還可能被誤用
- 結論
- 名字要取好啊owo,不要亂取
- 原則
- 變數名字寫完整
- function 要描述他的效果與目的(關心他「做什麼」),而不是他用什麼方式達到目的(而不是關心他「如何做」)
- ex:

### Side effect free function
- 我覺得他想強調「把重要的商業邏輯(領域邏輯)做成 Pure Function」將狀態與與運算分開來,減少副作用

### 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 包起來,只對外開出一個介面

### 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 的模型,來讓讓隱藏的概念轉變成顯示概念

### Pluggable component framework
- 從 interface 中分出一個 abstract core 並建立一個框架,讓框架透過 abstract core,可以自由替換實作介面
- 透過 pluggable component framework 可以讓系統自由與其他系統整合
- 但同時,也會限制 core domain 的發展,因為修改 abstract core 的成本更大
## 制定戰略的要點
- 決策必須傳達給整個團隊
- 決策必須收集回饋意見
- 計劃必須允許演變
- 架構團隊不必把所有最厲害的人都吸收進來
- 戰略儘量簡潔
## 結論
- 如果不是這本書翻譯的不好,就是 DDD 是一門玄學