領域驅動設計與整潔架構基礎課程筆記之一

今天開始,將會連續三天上領域驅動設計與整潔架構課程,講師是 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)的系統開發。

開始前先反問自己

  1. 學習或使用 DDD 最難的事是什麼?
  2. Domain Model 是不是越真實越好?

這段過程會帶來一些反思,我自己的結論是:常聽到一個名詞叫過度設計,也就是設計了太多沒被使用到的功能。

而我覺得 DDD 最困難的事,是如何把 Domain Model 設計的剛剛好。

這裡也剛好呼應到之前參加的建構微服務讀書會,這本書並沒有教大家「如何做」,而是教大家如何「取捨」。因此什麼是「微」服務?

「取捨得剛剛好的服務,就是微服務。」

什麼是 Domain?

以下應該是網路上或書上能找得到的資訊:

  1. 問題領域(Problem Domain)
    • 真實世界發生的問題。
    • 一般討論的時候,通常會省略成 Domain,如 Domain know how,指的就是問題領域。
  2. 解決方案領域(Solution Domain)
    • 以軟體來說,由人類產生並在機器裡執行的東西,就是解決方案領域。
    • Domain Model 是由人類產生,雖然它能夠協助表達問題領域,但它屬於 Solution Domain。
  3. 建模(Domain Modeling)
    • 需要領域專家跟開發人員(也有可能還要更多不同角色)共同建立起共識才能得到最適合的模型。
    • Domain Modeling means forming an opinion on how it behaves,建 Domain Model 的共識指的是,要形成對模型有共同的觀點。
    • 為什麼要建模?針對 Problem Domain 建模的目的是在於,要能夠在 Solution Domain 裡,表達 Problem Domain,進而透過 Solution Domain 提供的工具(如程式語言、框架等)來解決。
  4. 領域專家(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 老師的書:精實開發與看板方法

核心原則

這裡開始是複習看板方法。看板方法是一個漸近式改善流程的方法,它有三個核心原則:

  1. 可視化(Visualize)
    • 工作流程可視化
    • 工作項目可視化
  2. 限制半成品(Limit WIP)
    • 讓每一個階段的工作項目,限制在一個數量以下
    • 這是把 push model 變 pull model 的關鍵
  3. 管理工作流程(Manage Flow)
    • 透過指標數據來最佳化工作流程
    • lead time ,指的是交期或前置時間
    • cycle time,每個工作階段停留時間

註:所有 Agile 都是 pull model(「成員有時間再來做」就是一種 pull model)

分析軟體的使用案例

這裡是 Teddy 老師所準備的 ezKanban 的使用案例:

  1. 在 Dashboard 建立與瀏覽看板
  2. 建立看板後可以設計工作流程
  3. 設計好工作流程後,即可開始新增卡片
  4. 卡片可以評估時間、Assign 處理者
  5. 卡片可以設定 block ,並且要記錄被卡多久,如果解決了可以設定 unblock
  6. 每個工作流程可以設定 WIP Limit,當超過的時候不擋,但要警示
  7. 同個工作流程要能夠設定泳道(SwimLane)
  8. 需要能夠產生交期時間(Lead time)與階段時間(Cycle time)的報告
  9. 要能 Replay
  10. 多人協作功能

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 哥示範怎麼用,可以知道它的應用很自由,今天看到是另一種不同的用法。
  • 以前都是直接寫程式,這次學到如何建模。