Domain-Driven Design 領域驅動設計 - 我能 DDD 嗎?

tags: Domain-Driven Design
category: 軟體開發
description: Domain-Driven Design 領域驅動設計 - 我能 DDD 嗎?
created_at: 2024/11/05 17:00:00

cover image


前言

自上一篇 Domain-Driven Design 領域驅動設計 初學者指南 之後,接著重要的事情就是 「我能 DDD 嗎?」,這就好像我能寫測試嗎? 還是我的程式根本沒辦法測試?,這篇文章就是來稍微紀錄一下能不能 DDD 的一些可能可以參考的點和折衷方案。


預防針

老樣子先打個預防針,這些只是我在學習過程中消化得出的一些,希望可以翻譯成我的話變得更淺顯易懂(?),也許實際跑起來又有其他需要注意的地方,這些只是一個參考,不要當成是一個絕對的標準。


DDD 的前提

DDD 全名是 Domain-Driven Design,領域驅動設計,既然叫做「領域」驅動「設計」,那麼總是要有 「領域」 才能開始後續的事情,這就好比 Test-Driven Development,當然就是以「測試」來驅動「開發」,而你的開發基本上就是圍繞著測試來進行;換言之,領域驅動設計,則是你的設計基本上就是圍繞著「領域」來進行。

阿可是上一篇提到說領域簡單說就是你要解決的問題,或者說是專案做的事情,那不就每個專案都有領域,每個專案都可以 DDD 嗎?

嗯... 這樣說也許也沒什麼不對,所以 DDD 的前提也許不在「專案」上,而是「人」上,也就是說,你的團隊是否有能力去 DDD,這個能力不只是技術能力,還包含是否有領域專家等足夠了解你要解決的問題的人存在,並且有足夠的資源去支持這個專案。

別關掉阿,後面會進一步解釋


什麼樣的「人」能 DDD

這一段取自 Implementing Domain-Driven Design(IDDD) 一書:

你可以開始 DDD,如果你

  • 有開發卓越的軟體的熱情和毅力
  • 你渴望學習和進步
  • 你有能力去理解軟體模式,並知道如何應用他們
  • 有探索不同設計方法的能力和耐心
  • 勇於挑戰現狀
  • 注重細節,具備實驗與發現的渴望與能力
  • 尋求更聰明、更棒的程式碼

雖然列是列出這些特質,不過我想若有這些特質,其實也不只是能 DDD,而是能做任何事情,這些特質也許是你在做任何事情時都應該具備的。

做一個小註記,避免有人對軟體模式直接連結到軟體設計模式,雖然書上沒有明寫,但他的意思應該不只是軟體設計模式(如 GoF),還包含軟體架構模式(如 Clean Architecture),甚至是軟體開發模式(如 TDD),這些模式都是你在 DDD 過程中會遇到的。


什麼樣的「團隊」能 DDD

談完了「人」,人一多,形成了團隊,那麼什麼樣的團隊能 DDD 呢? 這裡先以比較宏觀的角度來看。

團隊建議的人數是不要太多也不要太少,太多的話溝通成本會增加,所以可能建議在 5~10 人左右,不過,就算只有一個人,我想只要有心,也是可以 DDD 的;只是你一個人自然就必須充當多個角色,時時刻刻提醒自己還在「領域」驅動的軌道上。

這裡就不先預設團隊的人數,而是列出一些團隊的特質(?)

  • 有領域專家(廢話
  • 技術自由度高
  • 願意傾聽、溝通良好、知識共享,願意互相學習
  • 對抗謬誤和認知偏差(?)
  • 高內聚、低耦合 (
  • 單一職責原則 (蛤*2
  • DDD 有共識和理解(廢話

接著針對上面一個一個做簡單的補充說明:


有領域專家

沒領域專家,你 DDD 個屁,這個就不多說了。 << 這句是 Copilot 寫的,他好兇QQ

總之肯定得有熟悉你要解決的問題的領域的人,能夠把這些領域的相關知識組織起來的人,你才有辦法透過 「領域」來驅動你的設計,而領域專家也可以是你自己,只要你對這個領域有足夠的了解。


技術自由度高

這裡指的技術自由度高不是說一直崇尚新技術,進而產生履歷驅動開發(?),而是因為畢竟 DDD 是屬於設計層級,開發是細節,這些細節應該要被推遲到後面,而如果技術沒得選擇,說不定你最後設計出來的東西根本做不出來或是只能用很髒的方式實現,反而本末倒置。

又或者是說有一些開發過程的模式可以幫助 DDD 落地的更好,但是卻礙於一些限制沒辦法使用,可能也會影響到 DDD 的實施。

比如說沒在 Code Review 也沒有做 CI 等檢查,後果就不贅述了,應該很明顯。


願意傾聽、溝通良好、知識共享,願意互相學習

這其實比較屬於團隊的文化,畢竟有些工程師可能不喜歡溝通或分享,這都會是一個阻礙。

尤其 DDD 是一個相互學習的過程,如果存在一種技術人員看不起(?)非技術人員(領域專家),直接認定你提的需求根本不可能做到或是不願意傾聽互相學習,那麼 DDD 也就無法進行。


對抗謬誤和認知偏差

這點其實也是和團隊,或者說整體風氣有關,有些時候人們會有一些認知偏差而做了不負責任(?)的決定,就如同前面所說 DDD 是一個互相學習的過程,也許大家都應該放下自己的偏見(?),一起針對問題進行討論,這樣才能達到最好的解決方案。

有可能被誤導的因素就好比:

嗯... Wiki 上內容有點多,簡單說就是有時候人們會被一些因素影響而做出不負責任的決定,這些決定可能會影響到 DDD 的實施;實際上這也不見得只會影響到 DDD,可能會影響到任何事情,有興趣可以自己嘗試套用的生活中,所以「批判性思考」就是一個很重要的能力。


高內聚、低耦合

這怎麼跟軟體設計原則扯上關係了? 嗯... 想一下反例,如果團隊到處耦合,溝通是需要成本的,是需要花時間花精力的,如果到處依賴,那豈不是根本沒有「團隊」的概念了,當初團隊希望不要太多人,就是為了避免溝通成本過高,而高內聚、低耦合也是為了避免溝通成本過高。

而且當依賴越少,也會減少部署需要的溝通成本,這也會提升團隊的效率。

這一點其實和 Boundary Context 有點像。


單一職責原則

嗯... 怎麼又又跟軟體設計原則扯上關係了? 嗯... 這其實是為了避免一個人負責太多事情,這樣會讓這個人無法專注在自己的領域上,電腦在處理多個事情時,會有Context Switch 的成本,人也是一樣,也許你神乎其技不受 Context Switch 影響,但我相信大多數人都會受到影響(?)


DDD 有共識和理解

這是肯定的,我相信應該不用多解釋吧(?)


對於團隊內的挑戰

剛才提完了相對宏觀的角度,現在要簡單說明團隊內部可能會遇到的挑戰。


領域專家

這個其實是最重要的一環,能參與的越完整越好,最忌諱的是開發專案時一直都沒有參與,直到最後驗收才跑出來的領域專家,這樣就準備互相給對方驚喜(?)

不過實際情況要找到一個能有那麼多時間參與的領域專家可能不容易。

如果今天情境是一個領域專家都找不到,那麼也許可以換個角度,未來是誰要用這個系統,是誰要來驗收,請來充當領域專家。


時刻確保團隊在 DDD 正確的道路上

就像上一篇所說的,我們希望說的話和程式碼一致,其實中間還有一個是模型,也就是三者要一致。

所以如果發現雖然有設計出一些模型,但是討論的時候從來不拿出來討論,沒有互相學習的過程,從雙向變回了以往的單向;或者是今天程式碼和模型脫鉤,明明領域專家在開會時能看懂模型在幹嘛,但是看不懂程式碼了,可能都需要做一些調整,因為這代表又出現了「翻譯」的過程,換句話說軟體準備又繼續增加不必要的複雜度。


開發人員的觀念轉換

有些開發人員可能開發多年,有些觀念就好比有些老人家一樣根深蒂固很難改變,但如果要做得好,可能就需要一些觀念的轉換。

必須從以「技術為中心考量」轉換為以「領域為中心考量」,這也是一個挑戰,必須從以往的「我要怎麼寫程式」,過度的關注技術細節,我要使用什麼工具、技術、套件等等,轉換為「我要解決什麼問題」,去多關注領域的問題,學著把這些「細節」推遲到後面。

然後說起話來也不能那麼的「技術」,要多講「通用語言」。

然後還有一個要拿捏好的地方,就是「保持程式碼的簡潔」,這並不是說你要寫的越簡潔越好,而是要保持程式碼的簡潔,不要過度的設計,什麼意思呢?

通常你重構之後,你做了抽象之後,也許會遺失掉一些領域邏輯(概念),這時領域專家可能就看不懂你在幹嘛了(就算目前還看的懂也別太開心),但這時總是會有一部份的程式碼重複,所以也許有些人會認為這樣不是違反了 DRY 原則嗎? 但其實沒有,DRY 原則是說不要重複你的邏輯,而不是說不要重複你的程式碼,這很重要。

如果真的不知道怎麼樣拿捏分寸,也許可以尊重你的「領域專家」,看他認不認同你這樣做,看看你做完之後是讓領域邏輯更清楚還是更糟。

這樣互相合作,也許就能夠達到一個平衡點。


技術能力的需求

這個其實是最基本的,但是也有一定的門檻在,如果團隊沒有足夠的技術能力,那麼 DDD 可能會變得很困難,畢竟牽扯到很多的軟體模式,這些都會是需要有的先備知識。


什麼樣的「專案」適合 DDD

咦,這不是前面說過了嗎? 每個專案都有領域,每個專案都可以 DDD,那麼這個小節要講什麼呢?

這裡其實是想說,有些專案可能不適合 DDD,有些可能根本是殺雞用牛刀,不符合成本效益,這裡就簡單列出一些可能不適合 DDD 的情況。

  1. 複雜度低
  2. 時間太短(資源不足)
  3. 生命週期短
  4. 領域不明確

複雜度低

假設你的系統只是一個單純的 CRUD 就可以解決,你可能不需要 DDD,你可能照你喜歡的寫法不管是 MVC 還是其他設計方式就可以輕鬆完成。

或者是你的系統真的很小,沒幾個 use case,也許你不需要 DDD,只會增加不必要的複雜度。 當然練習不在此限,畢竟是一個學習的過程


時間太短(資源不足)

假設時程真的很趕,要趕著上一版 MVP,這時可能不適合 DDD,因為 DDD 是一個需要時間的過程,需要不斷的討論、學習、實作,這些都需要時間,如果你的時間不夠,可能不適合 DDD,還是趕緊把 MVP 上線再說。

這時你可能需要的是一個折衷方案,也許可以先把 DDD 的一些概念先引入,但不一定要完全 DDD,等到有時間再慢慢轉過來,這在後面會介紹到。

至於為什麼會說資源不足,如果沒有那麼多的資源讓你有足夠的時間去落地,也是一個原因。 (這裡先撇除領域專家的問題)


生命週期短

如果你的專案只是用一次或是生命週期短,未來不太會有變動之類的,那麼可能就以前面提到的方式實現就好,不需要花費那麼多心思跟人力去 DDD,這樣可能會增加不必要的成本。 做了一堆 DDD 的設計,最後只用一次,這樣是不是有點浪費? 但練習也是不在此限。


領域不明確

這個其實是最重要的一點,如果你的領域不明確,可能不是你不想 DDD,而是你可能無從下手,畢竟你連自己要幹嘛都不知道,做出來的東西可能也很糟。


我的專案需要 DDD

那麼什麼專案適合 DDD 呢? 這裡就簡單列出一些可能適合的情況。

  1. 複雜度高,指的是領域邏輯複雜,而不是技術複雜,越複雜收益越高
  2. 時程允許,且專案生命週期長,未來也會有能力擁抱變化,生命週期越長收益越高

此外,在 IDDD 一書中,也提供了一個表格來檢視專案是否應該考慮使用 DDD

DDD Scoreboard DDD Scoreboard

簡單來說:

  • 0分: 你的軟體以資料為中心,只要 CRUD 就夠,不需要 DDD
  • 1分: 你的系統大概只有 25 ~ 30 左右的 use case,如果你可以使用你熟悉的工具快速開發完,沒有感受到痛苦,那麼也許不需要 DDD
    • 假設你有一個以 MVC 開發,那麼 Controller5 個函數,那算 5 個,而不是說你要有 25 ~ 30 個左右的 Controller 才能算 1 分。
  • 2分: 當你系統稍微大一些,有了 30 ~ 40 左右的 use case,這時也許複雜度會開始漸漸浮現,可以考慮使用 DDD
    • 通常可能沒辦法及時發現這些複雜度(常常低估),有部分原因是我們可能會只受限於常用的工具就直接做了。
  • 3分: 現在不複雜,但,之後呢? 如果已經有得到暗示(不管從領域專家或是其他方面),未來可能會變得複雜,那麼也許可以考慮使用 DDD
    • 這裡的暗示可能是來自於領域專家,也可能是來自於你自己的經驗,也可能是來自於其他方面。
  • 4分: 你無法預測接下來幾年功能會怎麼樣變化,你也無法預測這些變化只會是簡單的改變。
    • DDD可以幫助你管理軟體的複雜度,隨著時間你可以對軟體模型進行重構。
  • 5分: 你不了解軟體要處理什麼領域(問題),你的團隊也沒有人曾經從事關領域的(開發)工作,這時軟體可能很複雜。
    • 你需要領域專家一起工作了。

我有稍微翻譯一下,書中是說如果你分數加一加有 7 分以上就可以考慮使用 DDD 了。

但我是覺得只要你有「不確定」,就可以朝這個方向邁進,畢竟你總是要釐清問題理解問題,才有可能做出一個好的解決方案。

反之如果你很確定你要做的是什麼,其實這時也許你自己也充當了「領域專家」,其實你也在默默的 DDD,只是你可能沒有意識到而已。


折衷方案

有些時候礙於資源不足或時程壓力,也許沒辦法完整的讓 DDD 落地,這時也許可以考慮一些折衷方案,例如 DDD Lite

DDD Lite 是一個簡化版的 DDD,至於他算不算 DDD 這看法因人而異,但至少他是一個可以讓你在有限的資源下,也能夠讓你的專案變得相對於什麼都不做要好。

DDD Lite 通常會簡化一些概念,例如不強調通用語言,也不強調領域模型間的獨立與內聚性等,雖然寫起來一樣會有 Domain Model,但你的語言、模型、程式碼三者不一致,這樣也許會增加一些複雜度,但至少比什麼都不做要好,我的感受是就類似只學了 Clean Code 但總比什麼都不做好,不過會有點鬆散。

可能會有一點點的抽象,所以可以稍微複習一下軟體的複雜度是怎麼來的,可以回去參考上一篇 DDD文章外,這邊來舉另一個例子(?)

這個例子是跟統計或者是ML有關的例子(?)

假設我要做預測,我就是在求那個 function,這個 function 可能很複雜,假設這個 function 就是你這個專案的程式碼。

而做出來的專案,就是那個預測的結果,我們當然希望預測的結果和實際的結果越接近越好(最好0誤差,沒有多餘的熵),這就是我們的目標。

而我們要怎麼讓這個預測的結果和實際的結果越接近呢? 並且最好是讓這個模型的函數是可解釋的(領域專家看得懂),這就是 DDD 要解決的問題和目標。

而就算你沒辦法(可能沒資源之類)很漂亮的做到完整的 DDD,你至少可以盡可能讓結果相近一點,也是總比什麼都不做(亂猜)好。

如果文字有點複雜,可以想像你的需求是正方形,你亂做做出來的是星形,這肯定不太好,於是你可以花一點心思,把他做成圓形,這樣也許就好一點,這就是 DDD Lite;當然你也可以花更多心思,把他做成正方形,完美匹配,這就是 DDD

希望這樣的解釋可以讓你更容易理解(?)


結語

這篇文章主要是想說明一些 DDD 的前提,以及一些團隊可能會遇到的挑戰,以及一些專案可能不適合 DDD 的情況,以及折衷方案。

不過主要還是回到團隊間的討論是否要採納這種開發方式,是否有足夠的技術能力,是否有足夠的資源、時間,是否有能夠配合的領域專家;另外還要綜合考慮一下專案是否需要 DDD

不管怎麼樣,沒辦法預測未來軟體會怎麼樣的變動,能做的就只有盡可能讓未來修改的人感謝現在的你(?),而那個人很可能就是你自己。




最後更新時間: 2024年11月05日.