領域驅動設計與整潔架構基礎課程筆記之一
今天開始,將會連續三天上領域驅動設計與整潔架構課程,講師是 Teddy。
以下是關於此堂課程的筆記。
領域驅動設計原名為同名書 Domain-Driven Design,社群都簡稱為 DDD。整潔架構的原名為同名書 Clean Architecture。課程名稱為中譯,此筆記會用原文或縮寫表示。
DDD 是什麼?
DDD 全名為 Domain-Driven Design,意指我們先找出領域,有了領域接著就能驅動接下來的設計。其他如 TDD 是測試驅動開發,也就是先寫出測試,就能驅動我們去寫出 production code。
DDD 與 OOAD(Object-Oriented Analysis and Design)類似,主要在於實作細節不同。而 DDD 實作出來的結果,很適合目前流行的分散式(Distributed system)或事件驅動(Event-driven)的系統開發。
開始前先反問自己
- 學習或使用 DDD 最難的事是什麼?
- Domain Model 是不是越真實越好?
這段過程會帶來一些反思,我自己的結論是:常聽到一個名詞叫過度設計,也就是設計了太多沒被使用到的功能。
而我覺得 DDD 最困難的事,是如何把 Domain Model 設計的剛剛好。
這裡也剛好呼應到之前參加的建構微服務讀書會,這本書並沒有教大家「如何做」,而是教大家如何「取捨」。因此什麼是「微」服務?
「取捨得剛剛好的服務,就是微服務。」
什麼是 Domain?
以下應該是網路上或書上能找得到的資訊:
- 問題領域(Problem Domain)
- 真實世界發生的問題。
- 一般討論的時候,通常會省略成 Domain,如 Domain know how,指的就是問題領域。
- 解決方案領域(Solution Domain)
- 以軟體來說,由人類產生並在機器裡執行的東西,就是解決方案領域。
- Domain Model 是由人類產生,雖然它能夠協助表達問題領域,但它屬於 Solution Domain。
- 建模(Domain Modeling)
- 需要領域專家跟開發人員(也有可能還要更多不同角色)共同建立起共識才能得到最適合的模型。
- Domain Modeling means forming an opinion on how it behaves,建 Domain Model 的共識指的是,要形成對模型有共同的觀點。
- 為什麼要建模?針對 Problem Domain 建模的目的是在於,要能夠在 Solution Domain 裡,表達 Problem Domain,進而透過 Solution Domain 提供的工具(如程式語言、框架等)來解決。
- 領域專家(Domain Expert)
- 知道問題領域的答案的人(至少能講得出來)
什麼是 Design?
設計是決定 Form 與 Context 之間的邊界
— Christopher Alexandex
在上面這段話中,Form 指的是人類所建的產品,而 Form 以外的是 Context。決定 Form 與 Context 邊界的過程就是在做設計,而下這些決定的因素,必須要透過分析 force 才能得到。厲害的工程師寫程式在於,能夠精準分析出決定性的 force,並能夠針對這些 force 的力度做優先排序。
軟體的 force 通常都是 Non-functional requirement。如,可測性、可擴充性、可維護性,甚至 Deadline 也是一種 force。
這幾年來,研究過許多跟維護相關的主題,最終對於「程式設計」,我自己的想法是:把程式(Form)放到某個位置上後,會跟其他程式(Context)形成一個自然的邊界,因此:
「把正確的程式放在正確的位置,就是程式設計。」
實務分析需求
這裡跟看板相關的內容,跟 DDD 的流程沒有太大的關係,只是做為解釋 DDD 流程的一個範例。
因為 DDD 只是一個 Pattern 的集合,透過案例了解 DDD 可能會是比較簡單的方法。而課程是使用實作看板軟體做為問題領域的範例。
接著,第一步是要了解看板方法的需求為何,看法方法的細節可以參考 Ruddy 老師的書:精實開發與看板方法。
核心原則
這裡開始是複習看板方法。看板方法是一個漸近式改善流程的方法,它有三個核心原則:
- 可視化(Visualize)
- 工作流程可視化
- 工作項目可視化
- 限制半成品(Limit WIP)
- 讓每一個階段的工作項目,限制在一個數量以下
- 這是把 push model 變 pull model 的關鍵
- 管理工作流程(Manage Flow)
- 透過指標數據來最佳化工作流程
- lead time ,指的是交期或前置時間
- cycle time,每個工作階段停留時間
註:所有 Agile 都是 pull model(「成員有時間再來做」就是一種 pull model)
分析軟體的使用案例
這裡是 Teddy 老師所準備的 ezKanban 的使用案例:
- 在 Dashboard 建立與瀏覽看板
- 建立看板後可以設計工作流程
- 設計好工作流程後,即可開始新增卡片
- 卡片可以評估時間、Assign 處理者
- 卡片可以設定 block ,並且要記錄被卡多久,如果解決了可以設定 unblock
- 每個工作流程可以設定 WIP Limit,當超過的時候不擋,但要警示
- 同個工作流程要能夠設定泳道(SwimLane)
- 需要能夠產生交期時間(Lead time)與階段時間(Cycle time)的報告
- 要能 Replay
- 多人協作功能
EventStorming(事件風暴)
有了使用案例後,就可以開始想像這些案例會產生什麼事件,接著就能開始執行 Event Storming。
EventStorming 可以參考 Alberto Brandolini 所寫的電子書,或是參考 fx777 所撰寫的鐵人賽文章。
Big Picture 探索領域事件
EventStorming 有三個探索層級,依範圍大到小為:Big Picture、Process Modeling、Software Design。
首先第一件事是探索 Big Picture,指的是綜觀全局,大公司會與多個業務單位互動,或是流程本身很龐大的時候,需要先執行這個流程來探索所有有可能的 Domain Event。
- 公司有很多部門,部門間都會有邊界(參考穀倉效應),但在這個流程裡,大家都有辦法提供流程的部分線索。
- 知道全局很重要,不然對局部做最佳化不一定有效。
- 此層級通常不會轉換到程式碼,但能夠讓參與的人先對整個流程先有概觀。
這個階段執行其實很簡單,首先大家先各自找出有可能的 Domain Event。Domain Event 指的是領域事件,它是指:在問題域裡,當系統狀態發生改變,領域專家對這件事感興趣,它的描述必須是過去式,如:使用者已註冊。接著再依時間序把 Domain Event 依續從左邊往右排。傳統通常都是使用流程圖來表達流程,用 Domain Event 最大的好處在於:過去式能明確表達事件「結束」,而不需要猜測是「開始」還是「結束」。
只是這過程可能會非常發散,但這不是壞事,因為有可能其他伙伴知道大家所不知道的 Domain Event。通常會設定一個時間,在時間到之後再開始進行下一個階段。
下個階段是:把這些發散的 Domain Event 進行分類,分類的過程會遇到一些問題,如:同一個行為,但大家對它的用詞是不同的,這時就能跟大家統一用詞應該是什麼,並建立通用語言(Ubiquitous Language)。
再來可以應用 Hot spots、Systems、People(Actor / Persona)來輔助探索更多 Domain Event。
當完成探索 Domain Event 後,接著可以試著找出 Event 之間的 boundaries(邊界),有了邊界之後,就能夠畫出不同的區塊,進而找到不同區塊代表的領域。領域的種類如下:
- Core Domain(Core sub-domain)
- Generic Domain
- Support Domain
DDD 的書上有提到,Core Domain 是核心業務邏輯,換句話說,它是核心的問題領域;Support Domain 則是協助提高 Core Domain 的價值。
Generic Domain 如其名,是在描述通用的問題領域,我認為它也是 Support Domain 的一種。不同的場景可能會出現完全相同的問題領域,例如:身分驗證與授權,這就屬於「通用」領域。而通常開源或是第三方服務會有可能找得到解決方案,例如 OAuth 2 可以使用開源的 Hydra 或是花錢買第三方服務的 Auth0 等。
對我們的系統來說,身分驗證授權是 Generic Domain,但對於 Auth0 來說,則會是他們的 Core Domain。因此這個領域分類的方法,是會因場景而產生變化的。
一般來說,我們應該把最寶貴的人力資源(如:資深工程師),優先放在 Core Domain,因為它能解決的問題,正是我們服務的核心價值。接著才會是 Support Domain,最後才是 Generic Domain。
這並不是說 Support Domain 或 Generic Domain 不重要,而是這兩個 Domain 有其他的選擇,例如:Support Domain 可以讓初階工程師開發,而 Generic Domain 可以透過購買外部服務來完成。以 ChatGPT 為例,他們是使用 Auth0 作為登入系統,而 ChatGPT 團隊本身可以繼續專注在核心的模型開發。反過來說,Core Domain 代表的是公司的競爭力,因此不適合外包出去,像 ChatGPT 就不可能把他們模型開發外包出去。
Process Modeling
探索完領域後,接著可以針對單一流程來做 Modeling。最簡單的方法是,在 Domain Event 中,找一個最簡短且可以完成有意義的業務需求的流程,然後把所有 Domain Event 按順序先排成一個橫列。接著應用 Command、Read Model、Policy 等工具,來確認此流程是如何執行,最後流程確認完後,即可開始做 Modeling。
- Command 指使用者對系統下的命令
- Read model 的用法為:使用者可以參考 read model 來決定是否做 command。
- Policy 指當生某個特定 Event 時,要做哪些處理,如使用者註冊後,發通知信給使用者。(即 Observer Pattern)
要如何判斷 Model 完的結果符合需求?可以參考實例化需求,把需求的實例拿來看 Model 是否能夠表達,即可知道符不符合。
第一天就建模到一半結束了,蠻多東西都算是已知,有些是這次新學到的:
- EventStorming 的一種用法,從看書上用、自己亂用、Kim 哥示範怎麼用,可以知道它的應用很自由,今天看到是另一種不同的用法。
- 以前都是直接寫程式,這次學到如何建模。