實踐DDD領域驅動設計

說明

領域驅動設計最近又火了。概念不斷被說起,可是相信對於像筆者同樣的不少開發者對於其如何應用都一頭霧水。前端

正如《實現領域驅動設計》中做者提到的不一樣公司的業務能力開發能力和成熟度不同,DDD爲了解決複雜業務爲生,並不適合全部的軟件項目,對於不少初創公司而言,業務自己就是模糊的,只是須要作出一個MVP(最小可行性產品)來試探商業模式,採用ddd顯得過「重」了一點,反而給團隊成員帶來額外的負擔,因此團隊管理者首先應該關注的是軟件系統是否值得作出DDD投入數據庫

不過無論黑貓白貓,能抓到老鼠就是好貓。咱們以電商業務來演練和實踐ddd的部分理論,並解釋ddd的概念。編程

戰略設計階段

領域:即業務是屬於哪塊,電商領域,保險領域,零售領域,又可細化分爲子領域。如電商下(訂單交易領域、庫存領域、會員領域、物流領域....)後端

領域專家:通常指熟悉對應領域的產品經理項目經理架構

子域:子域可細分爲核心子域、通用子域和支撐子域,簡單理解爲哪部分是比較核心的就可稱做核心子域,哪些功能偏邊緣化叫支撐域。app

互聯網公司的敏捷開發模式的產品研發流程從收集需求、PRD評審、技術模塊拆分這些前期流程。而戰略設計即在這個階段完成,顧名思義側重於從宏觀上對業務進行拆分,和對將來走向的預測。框架

DDD的目的是爲了領域專家更好地與開發進行溝通合做,使得代碼更好地傳達業務規則。ide

最初的需求方可能會提一些凌亂的需求。spa

爲了更好地傳達規則,領域專家將需求整合提取出領域的概念,技術/項目管理者一塊兒劃分好子域和限界上下文。各方統一所謂的通用語言並在之後的協做中使用。好比電商業務中雙方約定好中商家用戶和買家用戶的概念,用戶和帳戶的概念,交易訂單和支付訂單、物流訂單、售後訂單的概念,以實現後期溝通順暢。設計

最終產出:劃分出了哪些子領域、上下文映射圖是怎樣的。

貼一個電商業務的上下文映射圖(下單交易上下文爲下游,其餘皆爲上游):

而各個上下文的交互方式,即集成限界上下文的方式其實就是咱們常說的系統交互方式:RPC調用、REST接口調用、消息隊列通訊。

怎麼理解限界上下文和子域的關係?

**限界上下文:**是一個顯示邊界,領域模型即存在於這個邊界以內。在邊界內,通用語言有特定明確的意義。

ps:是否是以爲每一個字都認得,可是不知道表達了什麼意思....

在理解限界上下文和子域上確實有點費勁。筆者當時的疑惑主要是:爲何要用上下文映射圖而不是子域交互圖,子域劃分出來不就說明邊界已經明確了麼,爲何還專門搞一個限界上下文的概念。

關於限界上下文,貼一下我的理解:

接下來要咬文嚼字一些了。

一、從「限界」二字來講。限界上下文明確了業務範圍和職責邊界。針對上面問題「子域中不是已經有邊界的概念了麼」。能夠思考一個有意思的事,子域有邊界,仍是說由於有了邊界纔有子域。聽到過一個很是到位的類比:若是沒有細胞壁,如何定義細胞質?

二、從「上下文」來講。上下文關注的是兩個系統交互時的環境,或者說語境。

舉個例子:小學是一個子域,中學是一個子域。升學這個事件動做則要上下文表達。

通用語言要在限界上下文(語境)中保證其明確意義。舉個例子,商家管理上下文中,咱們(平臺)說的用戶指的是商家而不是買家,支付上下文中,咱們以支付單爲核心,語境無需引入物流單、庫存等詞彙。

一般來講,咱們能夠近似地認爲子域和限界上下文一一對應的。

戰術設計階段

截止到此,咱們已經劃分好了子域,對開發人員來講,已經拆分好了項目。各個團隊能夠針對本身的子域進行獨立開發了。對於ddd而言,咱們開始進行戰術設計階段。戰略設計關心作什麼,戰術設計則更關心技術實現細節,即:怎麼作。

先說下ddd中推薦的六邊形架構

這裏要吐槽一下六邊形架構這個命名,搞得好像有六個什麼東西同樣。其實只是根據視覺形狀起的名。如今叫端口與適配器架構。

轉換一下是這樣的

**嚴格分層架構:**某層只能與直接位於其下方的層發生耦合

**鬆散分層架構:**容許上方層能夠與任意下方層發生耦合。

這裏採用鬆散分層架構。也可按依賴倒置原則, 基礎層依賴領域層的一些東西(常見的就是實體Entity)

適配器層:

負責接口轉換,便是最外層請求處理類,將外部請求轉化爲內部API能理解的輸入。對於REST接口多是一個controller類;
對於dubbo調用來講是開放出去的provider服務類;
對於grpc調用來講,是protobuf請求對象轉換處理類;
對於消息機制來講,對應的是消息的監聽器

如此採用端口適配器模式,能夠不影響內部服務,只需在適配器層進行增改。儘管你們不知道六邊形架構的定義,但相信不少人是這樣作的。無須贅述。

應用層:負責協調領域層的接口實現前端展現或返回須要。

領域層:定義領域實體和邏輯。包括實體、值對象、領域服務、領域事件、資源庫。

基礎層:如數據庫相關。

實體和值對象

實體:  
有惟一業務標識
有本身的業務屬性和行爲
屬性可變,有本身的生命週期

值對象: 
 能夠有惟一業務標識

有本身的業務屬性和行爲

一旦定義不可改變

兩者的關係可總結爲:值對象關心對象是什麼樣的,實體側重描述對象是哪一個?

提到有本身的業務屬性和行爲這塊,想一想這不就是咱們年輕時說的面向對象編程的思想碼?

可是回顧一下會發現,這個思想好像被不少人拋諸腦後好久了,定義的對象類都成了一個個的pojo,只有屬性和屬性對應getter和seter方法,也就是ddd中所說的失血模型。

**失血模型:**只含屬性和對應的getter/setter方法,無業務處理邏輯

貧血模型:包含不依賴持久化的部分領域邏輯,依賴持久的邏輯被放在領域服務層。

充血模型:絕大數業務邏輯都放在其中,包括持久化邏輯。少數不適合的邏輯被提取出來放在領域服務層中。

脹血模型:主張不須要領域服務層,把一些業務邏輯都放在模型對象中處理

領域服務

脹血模型主張把全部的業務邏輯放在模型中處理,可是事實上有些邏輯並非適合放在某個領域對象中處理。好比

一、領域對象間的轉換
二、某些場景下須要多個領域對象做爲輸入值,結果產生一個值對象。

區別於應用層的服務,領域服務處理的是業務邏輯。應用層負責對領域服務處理結果進行渲染和組裝返回給前端。ps: 針對查詢類的操做,建議做爲應用層的查詢服務單獨拎出來,由於查詢嚐嚐涉及到多個維度的查詢,或把多個領域對象的查詢結果組裝成一個返回值對象。

舉個栗子:訂單領域須要向商家(PC端)和買家(APP端),商家端按發貨條件時間查詢全部買家的訂單,操做發貨處理售後等流程。買家端完成下單、查詢我的訂單。在應用層咱們抽象三個應用處理出來,公用的查詢應用、商家端應用(關聯商家權限控制上下文)、買家端應用(關聯買家權限控制上下文)。

領域事件

領域事件即領域業務週期中一些關鍵行爲,關鍵的定義是其餘地方須要依賴此事件推送業務流轉。如訂單被支付這個事件,須要觸發庫存扣減、商家待結算帳戶餘額增長等操做。關於領域事件處理方法:跨子域處理經常使用消息隊列發佈/訂閱模式,同項目處理經常使用註冊事件監聽器處理。很少描述。

聚合和資源庫

領域對象之間經常有依賴關係。好比主訂單-子訂單(商品)項,子訂單依附於主訂單存在,兩者的關係稱做聚合,主訂單做爲聚合根。

@Data
public class TradeOrderEntity {
//訂單號
String orderNo;
// 商品詳情
List<OrderItemDetailEntity> orderItemDetails;

咱們經過爲每個聚合選擇一個根,並經過根來控制全部對邊界內的對象的訪問。外部對象只能持有根的引用;因爲根控制了訪問,所以咱們沒法繞過它去修改內部元素。因此在ddd中,資源庫Repository是面向聚合根操做的,可對多個dao對象的組合使用。

@Repository
public class TradeOrderRepository {

@Autowired
TradeOrderMapper tradeOrderMapper;

@Autowired
OrderItemDetailMapper orderItemDetailMapper;
}

關於其中一些思想,筆者也沒有想清楚,所謂知行合一,恐怕不少方法論要真的遇到問題了纔會理解其價值。先整理到這。最後針對Java後端開發同窗,提供一個模塊分層的框架供參考

思考

無忌,我教你的還記得多少?」 「回太師傅,我只記得一大半」

「 那,如今呢?」 「已經剩下一小半了」

「那,如今呢?」 「我已經把全部的全忘記了!」

「好,你能夠上了…」

相關文章
相關標籤/搜索