天天看點

領域驅動設計(DDD) 實踐之路(一)《架構篇》一、DDD是什麼?二、如何DDD 

正文

本文主要介紹了基于DDD思想,在生産項目中落地應用的實踐。本文為【DDD】系列文章中的第一篇,主要講述了戰略層面的DDD設計原則

一、DDD是什麼?

DDD是什麼?衆裡尋她千百度,蓦然回首,“DDD是一種可以借鑒的思想,而非嚴格遵循的方法論”.。

在日常軟體開發過程中,我們不可能在不了解産品形态的前提下進行軟體開發,在開發前,通常需要産品經理進行市場調研,确定産品一個大的方向架構,然後和老闆們開會讨論,最後敲定初步産品模型,進行大量的業務知識梳理,而後到達軟體設計的層面,最後才是開發。而在業務知識梳理的過程中,我們必然會形成某個領域知識,根據領域知識來一步步驅動軟體設計,就是領域驅動設計的基本概念。

1、正常軟體開發模式 VS 領域驅動設計模式

正常軟體開發模式一般有:

瀑布式

靈活式

瀑布式開發是一種傳統的軟體開發方法,嚴格遵循需求分析、産品設計、編碼、測試、維護等步驟順序,

這種模式下,開發人員很難從使用者那裡得到回報,預設了産品設計的這個業務模型就是正确的,導緻自由度降低,尤其在前期需求不明确的情況下,開發人員比較被動,導緻後期需求約為變更時候,難以調整或者成本較高。

後者在此基礎上進行了改進,它也需要大量的分析,範圍會設計到更精細的業務子產品,它是小步疊代,周期性傳遞,那麼擷取客戶的回報也就比較頻繁和及時。可靈活也不能夠将業務中的方方面面都考慮到,并且靈活是擁抱變化的,大量的需求或者業務模型變更必将帶來不小的維護成本,同時,對開發人員的要求也必然會更高。

DDD則不同:它像是更小粒度的疊代設計,它的最小單元是

領域模型(Domain Model)

,所謂領域模型就是能夠精确反映領域中某一知識元素的載體,這種知識的擷取需要通過與

領域專家(Domain Expert)

進行頻繁的溝通才能将專業知識轉化為領域模型。領域模型無關技術,具有高度的業務抽象性,它能夠精确的描述領域中的知識體系;同時它也是獨立的,我們還需要學會如何讓它具有表達性,讓模型彼此之間建立關系,形成完整的領域架構。通常我們可以用象形圖或一種

通用的語言(Ubiquitous Language)

去描述它們之間的關系。在此之上,我們就可以進行

領域中的代碼設計(Domain Code Design)

。如果将軟體設計比做是造一座房子,那麼領域代碼設計就好比是貼桌面。前者已經将房子的藍圖架構規劃好,而後者隻是一個小部分的設計:如果牆紙貼錯了,我們可以重來,可如果房子結構設計錯了,那修複成本就比較大了。

2、DDD之領域模型  

當我們面向業務開發的過程中,應該首先思考領域模型而不是如何建表。

作為開發人員大部分人都聽過一句話,“面試造航母、工作擰螺絲”,日常工作就是建表寫增删改查。之是以有這樣的認知,其根源在于其編碼過程中遵循的是表驅動設計思想而非領域驅動設計。前者隻能增加資料庫的表數量,而後者才會形成長期的、具有業務意義的模型,這樣的系統生命力才更加長久。我們也才能用工程的方法來編碼,從編碼轉身為業務域的領域專家、業務專家。在通過軟體實作一個業務系統時,建立一個領域模型是非常重要和必要的,因為領域模型具有以下特點:

  1. 領域模型是對具有某個邊界的領域的一個抽象,反映了領域内使用者業務需求的本質;領域模型是有邊界的,隻反應了我們在領域内所關注的部分;
  2. 領域模型隻反映業務,和任何技術實作無關;領域模型不僅能反映領域中的一些實體概念,如貨物,書本,應聘記錄,位址,等;還能反映領域中的一些過程概念,如資金轉賬,等;
  3. 領域模型確定了我們的軟體的業務邏輯都在一個模型中,都在一個地方;這樣對提高軟體的可維護性,業務可了解性以及可重用性方面都有很好的幫助;
  4. 領域模型能夠幫助開發人員相對平滑地将領域知識轉化為軟體構造;
  5. 領域模型貫穿軟體分析、設計,以及開發的整個過程;領域專家、設計人員、開發人員通過領域模型進行交流,彼此共享知識與資訊;因為大家面向的都是同一個模型,是以可以防止需求走樣,可以讓軟體設計開發人員做出來的軟體真正滿足需求;
  6. 要建立正确的領域模型并不簡單,需要領域專家、設計、開發人員積極溝通共同努力,然後才能使大家對領域的認識不斷深入,進而不斷細化和完善領域模型;
  7. 為了讓領域模型看的見,我們需要用一些方法來表示它;圖是表達領域模型最常用的方式,但不是唯一的表達方式,代碼或文字描述也能表達領域模型;
  8. 領域模型是整個軟體的核心,是軟體中最有價值和最具競争力的部分;設計足夠精良且符合業務需求的領域模型能夠更快速的響應需求變化;

3、DDD之通用語言 

由開發人員和領域專家通力合作開發出一個領域的模型是絕對需要的,但通常會由于一些基礎交流的障礙而存在難點。開發人員滿腦子都是類、方法、算法、模式、架構,等等,總是想将實際生活中的概念和程式工件進行對應。但是産品經理或領域專家不關心實作細節,隻用業務的術語來交談,不同的開發人員可能了解存在偏差,這樣很難保證最後項目的品質。

領域驅動設計的一個核心的原則是使用一種基于模型的語言。在事件風暴過程中,通過團隊交流達成共識的,能夠簡單、清晰、準确描述業務涵義和規則的語言就是通用語言。也就是說,通用語言是團隊統一的語言,不管你在團隊中承擔什麼角色,在同一個領域的軟體生命周期裡都使用統一的語言進行交流。,這種語言被稱為“通用語言(Ubiquitous Language)”。通用語言應該在模組化過程中廣泛嘗試以推動軟體專家和領域專家之間的溝通,進而發現要在模型中使用的主要的領域概念。

領域驅動設計(DDD) 實踐之路(一)《架構篇》一、DDD是什麼?二、如何DDD 

在事件風暴的過程中,領域專家會和設計、開發人員一起建立領域模型,在領域模組化的過程中會形成通用的業務術語和使用者故事。事件風暴也是一個項目團隊統一語言的過程。通過使用者故事分析會形成一個個的領域對象,這些領域對象對應領域模型的業務對象,每一個業務對象和領域對象都有通用的名詞術語,并且一一映射。微服務代碼模型來源于領域模型,每個代碼模型的代碼對象跟領域對象一一對應。通用語言确定了項目團隊内部交流的統一語言,而這個語言所在的語義環境則是由限界上下文來限定的,以確定語義的唯一性。

4、DDD之思考問題的角度

建立領域模型時也要将使用者置于模型之外

  1. 我們設計領域模型時不能以使用者為中心作為出發點去思考問題,不能老是想着使用者會對系統做什麼;而應該從一個客觀的角度,根據使用者需求挖掘出領域内的相關事物,思考這些事物的本質關聯及其變化規律作為出發點去思考問題。
  2. 領域模型是排除了人之外的客觀世界模型,但是領域模型包含人所扮演的參與者角色,但是一般情況下不要讓參與者角色在領域模型中占據主要位置,如果以人所扮演的參與者角色在領域模型中占據主要位置,那麼各個系統的領域模型将變得沒有差别,因為軟體系統就是一個人機互動的系統,都是以人為主的活動記錄或跟蹤;是以,當我們談及領域模型時,已經預設把人的因素排除開了,因為領域隻有對人來說才有意義,人是在領域範圍之外的,如果人也劃入領域,領域模型将很難保持客觀性。領域模型是與誰用和怎樣用是無關的客觀模型。歸納起來說就是,領域模組化是建立虛拟模型讓我們現實的人使用,而不是建立虛拟空間,去模仿現實。

5、DDD之分層架構

領域驅動設計(DDD) 實踐之路(一)《架構篇》一、DDD是什麼?二、如何DDD 

在《實作領域驅動設計》書中提到,DDD 分層架構有一個重要的依賴原則:“每層隻 能與位于其下方的層發生耦合。”

我們正常項目中的三層架構模型,大家都很熟悉,從上圖可以看出,三層架構向DDD四層架構演進的主要變化,在于邏輯層和資料通路層。

我們先來看一下業務邏輯層的變化。DDD 分層架構對三層架構的業務邏輯層進行了更 清晰的劃分,改善了三層架構核心業務邏輯混亂、代碼改動互相影響大的問題。DDD 分層 架構将三層架構業務邏輯層的業務邏輯拆分到了應用層和領域層,分别以應用服務和領域服務等形式存在。應用服務實作服務的組合和編排,領域服務完成核心領域邏輯,應用服 務可以快速響應前端業務和流程的變化,而領域層則更加專注領域模型和實作領域邏輯。

再來看一下資料通路層的變化。這個變化主要發生在資料通路層和基礎層之間。三層架構資料通路采用 DAO 方式,而 DDD 分層架構對資料庫等基礎資源通路時采用了 倉儲設計模式,領域層可以通過倉儲接口通路基礎資源的實作邏輯。這樣,通過依賴倒置 實作了各層對基礎資源的解耦。原來三層架構的第三方工具包、驅動、Common、Utility、Config 等通用的、公共的基礎資源統一放到了基礎層。

另外,DDD 分層架構在使用者接口層引入了 DTO 和 facade 接口,可以給前端應用提供 更靈活的資料和接口适配能力。

使用者界面/展現層

負責向使用者展現資訊以及解釋使用者指令。更細的方面來講就是:

  1. 請求應用層以擷取使用者所需要展現的資料;
  2. 發送指令給應用層要求其執行某個使用者指令;

應用層

很薄的一層,定義軟體要完成的所有任務。對外為展現層提供各種應用功能(包括查詢或指令),對内調用領域層(領域對象或領域服務)完成各種業務邏輯,應用層不包含業務邏輯。

領域層

負責表達業務概念,業務狀态資訊以及業務規則,領域模型處于這一層,是業務軟體的核心。

基礎設施層

本層為其他層提供通用的技術能力;提供了層間的通信;為領域層實作持久化機制;總之,基礎設施層可以通過架構和架構來支援其他層的技術需求;

二、如何DDD

DDD分為戰略設計和戰術設計。在戰略設計中,我們講求的是子域和限界上下文(Bounded Context,BC)的劃分,以及各個限界上下文之間的上下遊關系。目前如此火熱的“在微服務中使用DDD”這個命題,究其最初的邏輯無外乎是“DDD中的限界上下文可以用于指導微服務中的服務劃分”。事實上,限界上下文依然是軟體子產品化的一種展現,與我們一直以來追求的子產品化原則的驅動力是相同的,即通過一定的手段使軟體系統在人的大腦中更加有條理地呈現,讓作為“目的”的人能夠更簡單地了解進而掌控軟體系統。

如果說戰略設計更偏向于軟體架構,那麼戰術設計便更偏向于編碼實作。DDD戰術設計的目的是使得業務能夠從技術中分離并突顯出來,讓代碼直接表達業務的本身,其中包含了聚合根、應用服務、資源庫、工廠等概念。雖然DDD不一定通過面向對象(OO)來實作,但是通常情況下在實踐DDD時我們采用的是OO程式設計範式,行業中甚至有種說法是“DDD是OO進階”,意思是面向對象中的基本原則(比如SOLID)在DDD中依然成立。

戰略模組化包括:界限上下文(Bounded Context)、上下文映射圖(Context Mapping)。

戰術模組化包括:聚合(Aggregate)、實體(Entity)、值對象(Value Objects)、資源庫(Repository)、領域服務(Domain Services)、領域事件(Domain Events)、子產品(Modules)。

接下來我們結合界限上下文,Repository等幾個關鍵的地方來進一步闡述,如何DDD。如何去實作,可以根據實際情況具體分析。我覺得戰略DDD比戰術DDD更重要,我想這就是DDD作為一種思想的神奇所在。如同金庸筆下的少林絕學易筋經一樣,一套并無明确招式的内功心法卻能打遍武林。

1. 界限上下文的了解

限界上下文的概念很重要,剛開始學習DDD的時候,不是很了解這個概念,隻是隐約記得什麼限定上下文、是實體、值對象和領域服務等幾個名詞,其實限界上下文感覺更清楚,限的意思就是劃分、規定,界就是界限、邊界,上下文就是業務的整個流程,總結起來就是限界上下文是 在劃定的界限中的一個業務流程,同時對于業務的描述是通過通用語言來表述的,限界上下文和通用語言的關系就是:在一個特定的限界上下文隻使用一套通用語言,并且保證它的清晰性和簡潔性。

首先,領域可以拆分為多個子領域。一個領域相當于一個問題域,領域拆分為子域的過程就是大問題拆分為小問題的過程。在上圖中,項目被拆分為存儲、訂單、轉寫 三個子域;子域還可以根據需要進一步拆分下一級子域,比如轉寫域還可以拆分為識别和字幕等子域,各類子域的邊界,就是限界上下文的邊界了。同時,子域可能會包含多個限界上下文,比如在存儲域,包括存儲域、使用者額度等相關行為域,分享域等。

每個領域模型都有它對應的限界上下文,團隊在限界上下文内用通用語言交流。領域内所有限界上下文的領域模型構成整個領域的領域模型。理論上限界上下文就是微服務的邊界。我們将限界上下文内的領域模型映射到微服務,就完成了從問題域到軟體的解決方案。可以說,限界上下文是微服務設計和拆分的主要依據。在領域模型中,如果不考慮技術異構、團隊溝通等其它外部因素,一個限界上下文理論上就可以設計為一個微服務。