天天看點

圖解DDD模組化六個問題與六個步驟

圖解DDD模組化六個問題與六個步驟

0 文章概述

領域驅動設計DDD是一段時間以來比較流行的概念,剛開始接觸時覺得概念很多,比較難以落地。本文就來分析探讨DDD落地時需要關注的六個問題,并通過一個足球運動員資訊管理系統案例分析落地的六個步驟。

圖解DDD模組化六個問題與六個步驟

1 六個問題

DDD方法論的核心是将問題不斷分解,把大問題分解為小問題,大業務分解小領域,簡而言之就是分而治之,各個擊破。

分而治之是指直接面對大業務我們無從下手,需要按照一定方法進行分解,分解為高内聚的小領域,使得業務有邊界清晰,而這些小領域是我們有能力處理的,這就是領域驅動設計的核心。

各個擊破是指當問題被拆分為小領域後,因為小領域業務内聚,其子領域高度相關,我們在技術次元可以對其進行詳細設計,在管理次元可以按照領域對項目進行分工。需要指出DDD不能替代詳細設計,DDD是為了更清晰地進詳細設計。

在微服務流行的網際網路行業,當業務逐漸複雜時,技術人員需要解決如何劃分微服務邊界的問題,DDD這種清晰化業務邊界的特性正好可以用來解決這個問題。

我們的目标是将業務劃厘清晰的邊界,而DDD是達成目标的有效方法之一,這一點是需要格外注意的。DDD是方法不是目标,不需要為了使用而使用。例如業務模型比較簡單可以很容易分析的業務就不需要使用DDD,還有一些目标是快速驗證類型的項目,追求短平快,前期可能也不需要使用領域驅動設計。

領域可以劃分多個子領域,子域可以再劃分多個子子域,限界上下文本質上也是一種子子域,那麼在業務分解時一個業務子產品到底是領域、子域還是子子域?

我認為不用糾結在這個問題,因為這取決于看待這個子產品的角度。你認為整體可能是别人的局部,你認為的局部可能是别人的整體,叫什麼名字不重要,最重要的是按照高内聚的原則将業務高度相關的子產品收斂在一起。

業務劃分粒度的粗細并沒有統一的标準,還是要根據業務需要、開發資源、技術實力等因素綜合考量。例如微服務拆分過細反而會增加開發、部署和維護的複雜度,但是拆分過粗可能會導緻大量業務高度耦合,開發部署起來是挺快的,但是缺失可維護性和可擴充性,這需要根據實際情況做出權衡。

領域對象與資料對象一個重要的差別是值對象存儲方式。在讨論領域對象和資料對象之前,我們首先讨論實體和值對象這一組概念。實體是具有唯一辨別的對象,而唯一辨別會伴随實體對象整個生命周期并且不可變更。值對象本質上是屬性的集合,并沒有唯一辨別。

領域對象在包含值對象的同時也保留了值對象的業務含義,而資料對象可以使用更加松散的結構儲存值對象,簡化資料庫設計。

現在假設我們需要管理足球運動員資訊,對應的領域模型和資料模型應該如何設計?姓名、身高、體重是一名運動員本質屬性,加上唯一編号可以對應實體對象。跑動距離,傳球成功率,進球數是運動員比賽中的表現,這些屬性的集合可以對應值對象。

值對象在資料對象中可以用松散的資料結構進行存儲,而值對象在領域對象中需要保留其業務含義如下圖所示:

圖解DDD模組化六個問題與六個步驟

我們根據圖示編寫領域對象與資料對象代碼:

抽象的核心是找相同,對不同僚物提取公因式。實作的核心是找不同,擴充各自的屬性和特點。例如模闆方法設計模式正是用抽象建構架構,用實作擴充細節。

我們再回到資料模型的讨論,可以發現腳本化是一種拓展靈活性的方式,腳本化不僅指使用groovy、QLExpress腳本增強系統靈活性,還包括松散可擴充的資料結構。資料模型抽象出了姓名、身高、體重這些基本屬性,對于頻繁變化的比賽表現屬性,這些屬性值可能經常變化,甚至屬性本身也是經常變化,例如可能會加上射門次數,突破次數等,是以采用松散的JSON資料結構進行存儲。

2 六個步驟

工程理論總是要落地的,落地也是需要一些步驟和方法的。本文我們一起分析一個足球運動員資訊管理系統,目标是管理運動員從轉會到上場比賽整條鍊路資訊,這個系統大家應該也都沒有接觸過,我們一起來分析。需要說明本執行個體着重示範DDD方法論如何落地,業務細節可能并不能面面俱到。

梳理流程有兩個問題需要考慮,第一個問題是從什麼視角去梳理?因為不同的人看到的流程是不一樣的。答案是取決于系統需要解決的是什麼問題,因為我們要管理運動員從轉會到上場比賽整條鍊路資訊,是以從運動員視角出發是一個合适的選擇。

第二個問題是對業務不熟悉怎麼辦?因為我們不是體育和運動專家,并不清楚整條鍊路的業務細節。答案是梳理流程時一定要有業務專家在場,因為沒有真實業務細節,無法領域驅動設計。同理在網際網路梳理複雜業務流程時,一定要有對相關業務熟悉的産品經理或者營運一起參與。

假設足球業務專家梳理出了業務流程,運動員提出轉會,協商一緻後到新俱樂部體檢,體檢通過就進行簽約。進入新俱樂部後進行訓練,訓練名額達标後上場比賽,賽後參加新聞釋出會。

圖解DDD模組化六個問題與六個步驟

四色模組化第一種顔色是紅色,表示時标對象。時标對象是四色模組化最重要的對象,可以了解為核心業務單據。在業務進行過程中一定要對關鍵業務留下單據,通過這些單據可以追溯出整個業務流程。

時标對象具有兩個特點:第一是事實不可變性,記錄了過去某個時間點或時間段内發生的事實。第二是責任可追溯性,記錄了管理者關注的資訊。現在我們分析本系統時标對象有哪些,需要留下哪些核心業務單據。

轉會對應轉會單據,體檢對應體檢單據,簽合同對應合同單據,訓練對應訓練名額單據,比賽對應比賽名額單據,新聞釋出會對應采訪單據。根據分析繪制如下時标對象:

圖解DDD模組化六個問題與六個步驟

這三類對象在四色模組化中用綠色表示,我們以電商場景為例進行說明。使用者支付購買商家的商品時,使用者和商家是參與方。物流系統發貨時配送單據需要有配送位址對象,位址對象就是地。訂單需要商品對象,物流配送需要有貨品,商品和貨品就是物。

我們分析本例可以知道參與方包含總經理、隊醫、教練、球迷、記者,地包含訓練位址、比賽位址、采訪位址,物包含簽名球衣和簽名足球:

圖解DDD模組化六個問題與六個步驟

在四色模組化中用黃色表示,這類對象表示參與方、地、物是以什麼角色參與到業務流程:

圖解DDD模組化六個問題與六個步驟

我們可以為對象增加相關描述資訊,在四色模組化中用藍色表示:

圖解DDD模組化六個問題與六個步驟

在四色模組化過程中我們體會到時标對象是最重要的對象,因為其承載了業務系統核心單據。在劃分領域時我們同樣離不開時标對象,通過收斂相關時标對象劃分領域。

圖解DDD模組化六個問題與六個步驟

當業務系統發生一件事情時,如果本領域或其它領域有後續動作跟進,那麼我們把這件事情稱為領域事件,這個事件需要被感覺。

例如球員比賽受傷了,這是比賽子域事件,但是醫療和訓練子域是需要感覺的,那麼比賽子域就發出一個事件,醫療和訓練子域會訂閱。

例如球員比賽取得進球,這也是比賽子域事件,但是訓練和合同子域也會關注這個事件,是以比賽子域也會發出一個比賽進球事件,訓練和合同子域會訂閱。

通過事件互動有一個問題需要注意,通過事件訂閱實作業務隻能采用最終一緻性,需要放棄強一緻性,這一點可能會引入新的複雜度需要權衡。

圖解DDD模組化六個問題與六個步驟

接口層:提供面向外部接口聲明和DTO對象

通路層:提供HTTP通路入口

業務層:領域層和業務層都包含業務,但是用途不同。業務層可以組合不同領域業務,并且可以增加流控、監控、日志、權限控制切面,相較于領域層更為豐富,提供BO對象

領域層:提供DMO(DomainObject)、VO、事件、資料通路對象,核心是按照領域進行分包,領域内高内聚,領域間低耦合

外部通路層:在這個子產品中調用外部RPC服務,解析傳回碼和傳回資料

基礎層:包含基礎功能,例如緩存工具,消息隊列,分布式鎖,消息發送等功能

圖解DDD模組化六個問題與六個步驟

我們展開領域層進行分析。領域層的核心是按照領域進行分包,并且提供DMO、VO、事件、資料通路對象,領域内高内聚,領域間低耦合,例如domain1對應合同子域,domain2對應訓練子域,domain3對應合同子域。

圖解DDD模組化六個問題與六個步驟

目前為止領域已經确定了,現在可以根據領域劃分任務了,組内成員分别負責一個或多個領域進行詳細設計,這個階段就是大家非常熟悉的用例圖,活動圖,時序圖,資料庫設計,接口設計的用武之地。需要說明的是領域驅動設計不是取代詳細設計,而是為了更清晰地詳細設計。

3 文章總結

本文探讨了DDD落地時需要關注的六個問題,并通過一個足球運動員資訊管理系統案例落地了六個步驟。在實際應用中各業務形态千差萬别,但是方法論卻可以通用,我們需要明确DDD核心是分而治之各個擊破,并配合一些經過檢驗有效方法進行模組化,希望本文對大家有所幫助。

圖解DDD模組化六個問題與六個步驟