軟體架構設計 與 DDD 相關問題的答客問

軟體架構設計 與 DDD 相關問題的答客問

圖片來源:https://c4model.com/

前言 由於我最近蠻多文章在介紹 Clean Architecture 與 DDD 領域驅動設計與開發以及 C4 Model,但是套用到他目前的專案中卻不知道如何開始?再者,TDD 只是概念上了解,但是在真實的專案中如何開始?包括我的 OOAD 課程中使用的 Modeling 的分析方法在 TDD 下真的也可以使用?UML 不會回歸 Waterfall 嗎?這些讓人一頭霧水?

問題一:

您部落格中提到的 C4 Model 的概念究竟是什麼?我現在在現有專案中要怎麼使用?


回答: 

我在前一篇文章中提到了 Simon Brown 的 C4 Model,也是我在當時 DDD 的分享中主要內容之一。

圖(一)、以決戰 OOAD 課程中的線上房貸申請系統為例的 C4 Model 以決戰 OOAD 課程中的線上房貸申請系統為例的 C4 Model

為什麼當時我會特別提到 C4 Model?主要是因為, 我覺得他的概念不錯的地方在於它是走 DDD 的角度來發展的,很多人說他為什麼要另外創創造一個表示法?部分比較傳統的開發者甚至是批評他的內容是四不像,但是這我覺得其實是種誤解,因為其實作者有說明 C4 其實沒有限定使用哪一種表示法,我覺得 C4 最棒的地方(也是有些朋友會誤解的)在於它將軟體分為 4 個層次(Level),這四個 Level 並不是指分層的結構而是作者用一種抽象化的方式將軟體從最外的 System Context 視角(C4的System Context 也可對應到 DDD 的 Bounded Context),由外一塊塊的往內窺視,因為作者覺得軟體架構就像 Google Map 一樣,由最頂端往下看就是一顆地球,接著放大某些視角,就可以看見台灣,再放大台北市就可以看見都市、接著再放大就會看見街道,而街道 = Code ,就是最細節的部份

套用 C4 的最主要目的,其實是希望透過這樣的『抽象』來建立一個【領域專家】與【開發團隊】溝通沒有隔閡的通用語言(Ubiquitous Language)、來讓『技術視角』與『業務視角』的溝通可以像 Google Map 視角一般,開發人員可以看見要建造哪一個『街道』,而領域專家也可以 看到整個地球的『哪一個國家的(街道)會被建造或是修改』。

而透過這樣的抽象,對應到軟件架構,讓我們快速可以反映架構的調整,進而做到高可用的軟件架構。

至於如果要在現有專案中使用的話,我的回答是這樣的:就如同我上方所提及的,C4 Model 使用一種抽象化的方式,將軟體架構建立 4 個層次的視角,但這不是一蹴可及的,你得先從基礎的『重構』開始做起,並搭配『單元測試』一起進行,專案的 執行/開發 過程中一定會頻繁的重構,可能因為需求的調整、或擴增的需求、又或者因為 Bug Fix,因為不管是清理遺留代碼,或者是增加可擴展性,重構都是必須的項目,當然,重構的過程當中得一定要有 Unit Test 的保護之下,你的重構的動作才是安全的,因為我可以很短的時間內快速的驗證商業需求是否仍然正常,沒有因為重構的動作而被改壞,這是確保專案開發過程中能夠『短時間的快速反應』並『達成敏捷』的最基本條件,這真的至關重要。關於如何讓軟體架構變得敏捷,先前我有撰寫許多相關文章:http://gelis-dotnet.blogspot.com/2019/06/blog-post.html您的軟體架構構敏捷嗎?(二)-持續演進的軟體架構〕其實說穿了就是持續有撰寫單元測試的重構下才有短時間應付變化的能力,若能進一步以 TDD 為開發的方法效果會更好,當然門檻也越高,但是這也更適合在敏捷的環境下。

做到上方的要求只是第一個門檻,接著你得加強與實踐相關稍進階的工程方法,像是我課程中提到的 MDA/MDE/MDD 或者是 TDD/BDD/ATDD 甚至是 SBE 實例化需求等等,這些都需要一些抽象化思考的能力,但落實在程式碼上又需要與實際的工程方法結合與最基礎的 OOA 與 OOD 等等,這部分可參考先前的文章:DDD 中為人所忽略的『軟體工程』與『工程實踐』方法

以 DDD 來說,MDA 是將您的抽象化的 Domain Modeling 對應到程式碼的一種方式之一。至於 Domain Modeling 使用哪一種圖形表示法在 C4 Model 並沒有限定種類,只是 MDA 基於 UML 上。

圖(二)、OMG 提出的 MDE 模型驅動工程 

在MDA上定位的BPMN 


圖片來源:https://research.linagora.com/pages/viewpage.action?pageId=3639295

當然你也可以使用 BPMN,但 BPMN 仍然是建構在 MDA 的 CIM (Computation Independent Model)上。

關於 MDA (model Driven Architecture) 的細部解釋可參考 決戰 OOAD 系列課程(線上版) 內有一個章節針對 MDA 有詳細的說明 & 實務開發。

傳送門: https://hiskio.com/courses/347/about?promo_code=437QJMG

當時有分享到 C4 Model 的演講投影片如下:

https://www.slideshare.net/GelisWu/clean-architecture-239574954


問題二: 

如何將它現在正在實作的專案改成支援 Clean Architecture 的軟體架構?


回答: 

若要將現有專案調整成乾淨架構 Clean Architecture 得先從基礎的『分層』開始,簡單的說,你得先將程式碼放置在『適合』的地方,這個適不適合是基於 Clean Architecture 角度來切割的,先把程式碼放置在對的地方後,接著是解決『相依性問題』。

幾個例子: 先將 DataAccess 抽離出來放置在 Infrastructure Layer 裡,這時你得抽離原本 Website 直接相依 DataAccess 問題,需改成相依 Infrastructure,然後建立 Application 層將 Business 業務邏輯抽離出來放置在 Application 裡,這裡就有很多程式碼重構等問題,包括拆 Libraries Project 抽離 Interfaces 等等,以上是如果你原先將業務邏輯寫在 Website 上的話,如果你原本就是有 Web API 與 明確的前後端 (Front-End Framework/Server Side) 的處裡或甚至 SPA 的應用架構的話以上這些動作會更容易。

再來,如果你原本沒有 Repository 得針對 Business 抽離並實作對應的 IRepository 比如客戶資料管理 ICustomerRepository 等並放置在 Domain 層,所以你必須將 Domain 層建立起來,把 ICustomerRepository放置在 Domain 層,並讓 Infrastructure 參考 Application,而因為 Application 本來就需要參照 Domain 所以自然能夠在 Infrastructure 裡實作 ICustomerRepository 的實體。

最後再將抽離出來的 Entities 放置在 Domain 裡面。

針對如何修改遺留代碼與讓它更好維護可參考這本書:

圖(三)、修改軟件的藝術-建構易維護代碼的9條最佳實踐 

修改軟件的藝術-建構易維護代碼的9條最佳實踐


讀這本書建議搭配 『重構-改善既有程式碼設計』與『Clean Code 無暇的程式碼』與『Clean Archiecture 無瑕的程式碼:整潔的軟體設計與架構篇』一起服用效果更佳。

關於 Clean Architecture 可參考我在 DDD 社群分享的內容的 Sample Code:

https://github.com/wugelis/DDDTaiwanLab


問題三: 

如何開始撰寫單元測試?如果是他手上的這個專案為例,要從哪裡開始?


回答: 

2023-11-22 (更新)

由於已經事過境遷,針對問題三,有些回覆已不符合現在情況,我現在稍作修正,來做比較全面性的回答。

這問題可以分兩部分

1). 第一個部分針對 Legacy System 的如何撰寫單元測試?

因為針對『如果是他手上的專案為例』這不是立即能進行 TDD 或者馬上會有適合的切入點。所以,原先的回答比較是針對 Legacy System。

這個問題,我得先解釋在現有系統中撰寫單元測試有兩個主要好處:

  • 了解系統的複雜度
  1. 系統複雜度越高,代表職責越容易牽扯在一起
  2. 複雜度高,也代表不好維護,通常是重構的優先選擇
  3. 重構的過程,同步撰寫測試,在測試的保護下的重構才是安全的重構
  • 解決外部依賴
  1. 外部依賴越強,代表耦合性越強,代表越難撰寫與進行測試
  2. 如果因為外部依賴強,導致撰寫(測試/模擬)成本過高,那麼就建議直接撰寫整合測試
  3. 在有整合測試的保護下,後續對 Legacy Code 撰寫 Unit Test 才有個後盾

若真的不知道從何著手,一個是可先從系統中修改幅度最高的業務邏輯開始撰寫單元測試、第二個就是針對容易出現問題的業務邏輯(相關的功能切入尋找共用性最高的業務邏輯)來開始先撰寫、第三就是找我上單元測試 UnitTest 與 Moq 物件實務課程 XDDD

可參考先前實體課程-傳送門: https://mystudyway.kktix.cc/events/softshare-unit-test-01


2023-11-22 (更新)

2). 這是第二的部分對於 DDD 與 使用 Clean Architecture 的系統,反而能夠更容易的『只針對〔邏輯/Domain Layer〕做測試』,這裡也可以在後來的文章『軟體架構設計:無題』中解釋這部分怎麼進行,包括使用 TDD 驅動開發時,透過需求來驅動整個架構的拓展,對於一個容易擴展的架構該用哪一種架構最適合呢?答案當然是 Clean Architecture 或者是六角架構了。


我後來的文章『軟體架構設計:無題』裡有完整的呈現這個部分,有興趣的可閱讀此篇文章。


而完整的範例可參考:

https://github.com/wugelis/AdapterInOutLayerSapmle



問題四: 

接續上題、另外 TDD 開發方法在 DDD 領域驅動開發上如何使用?


回答: 

DDD 領域驅動開發是一個相當龐大的主題,它提供了『戰略(Strategic)』與『戰術(Tactical)』兩個方向的指導方針,簡單說、戰略除了對團隊開發合作甚至是協助企業如何運用資源外,也提供一種思考模式,稍微有別於傳統 OOAD 的思考模式。而戰術則提供建模、分析商業需求與捕捉領域知識,如何尋找領域模型與界定系統邊界的指導方針,可謂博大精深,我無法在這裡解釋完畢,未來再另闢章節完整介紹 DDD。

圖(四)、DDD 戰略建模 

DDD 戰略建模


TDD 的部分,有追我文章的讀者應該會有些概念才對,這裡就不再詳述,不熟悉可先參考【敏捷的軟體架構設計:可擴展的軟體架構】 [http://gelis-dotnet.blogspot.com/2019/04/blog-post_22.html] 與 【您的軟體架構夠敏捷嗎?】[http://gelis-dotnet.blogspot.com/2019/04/blog-post.html] 以及【您的軟體架構夠敏捷嗎?(三)- 使用 TDD 實現最後的設計】[http://gelis-dotnet.blogspot.com/2020/05/blog-post.html] 等、簡單的說,有了 Domain Model 再開始撰寫紅燈測試。

圖(五)、通用語言 Ubiquitous Language

領與專家 與 開發團隊 先有通用語言 Ubiquitous Language 後,建模時別忘了重要的技術實踐,程式碼可以對應到領域模型、在 Unit Test 的保護之下,基於 Clean Architecture 方可以真正實踐可擴展的商業模型的程式碼。


最後: 

這些實踐是一條漫漫長路,不是短時間內一蹴可及的,相關軟體工程實踐方法都不可少,反而要更(精煉/熟悉)這些原本的開發技術,因為國外在談敏捷的大師們通常都已經是撰寫程式的高手,他們談敏捷只是想解決如何讓這些高手們在同一個團隊一起工作而已。




關於 Gelis:

資深 .NET 技術顧問

FB 社團 (軟體開發之路):

https://www.facebook.com/groups/361804473860062/

FB 粉絲團 (Gelis 的程式設計訓練營):

https://www.facebook.com/gelis.dev.learning/

我講授過的課程 SlideShare:

https://www.slideshare.net/GelisWu


以下是我經營的項目與內容:

(1). 企業內訓課程

(2). 專業顧問


企業內訓課程:

1. .NET Core 3.1 從入門到進階

先前實體課程連結

2. 跨平台的 Web API Framework 框架設計

先前實體課程連結

3. 決戰 OOAD 系列課程 - 使用 UML

先前實體課程連結

4. 單元測試 UnitTest 與 Moq 物件實務課程

先前實體課程連結

5. 快速開發系列 - C# Project Templates 範本設計

留言

這個網誌中的熱門文章

軟體架構設計:API 設計準則(二)、API Design-First 原則、策略與開發流程

常見的程式碼壞味道(Code Smell or Bad Smell)

什麼是 gRPC ?