推薦:講給P8聽的業(yè)務(wù)設(shè)計(jì)課:DDD領(lǐng)域驅(qū)動設(shè)計(jì),架構(gòu)師必會技能(一個案例讓你透徹理解DDD)

一、引子
不知今年吹了什么風(fēng),忽然DDD領(lǐng)域驅(qū)動設(shè)計(jì)進(jìn)入大家視野。該思想源于2003年 Eric Evans編寫的“Domain-Driven Design領(lǐng)域驅(qū)動設(shè)計(jì)”簡稱DDD,Evans DDD是一套綜合軟件系統(tǒng)分析和設(shè)計(jì)的面向?qū)ο蠼7椒?。剛好公司領(lǐng)導(dǎo)強(qiáng)力推薦這個,抱著學(xué)習(xí)的心態(tài),耗時5個月,體驗(yàn)了一把:“DDD從入門到棄坑”。
二、思想
學(xué)習(xí)網(wǎng)站:https://www.jdon.com/ddd.html
2.1 服務(wù)器后端發(fā)展三個階段

服務(wù)器后端發(fā)展三個階段:
- 面向過程腳本:初始簡單,業(yè)務(wù)復(fù)雜后,維護(hù)難度指數(shù)上升。-->基本不為主流使用
- 面向數(shù)據(jù)庫表:初始難度中,業(yè)務(wù)復(fù)雜后,維護(hù)難度延遲后再指數(shù)上升。--->目前市面上主流
- 面向業(yè)務(wù)模型:DDD+SOA微服務(wù)的事件驅(qū)動的CQRS讀寫分離架構(gòu):應(yīng)付復(fù)雜業(yè)務(wù)邏輯,以聚合模型替代數(shù)據(jù)表模型,以并發(fā)的事件驅(qū)動替代串聯(lián)的消息驅(qū)動。真正實(shí)現(xiàn)以業(yè)務(wù)實(shí)體為核心的靈活拓展。初始難度高,業(yè)務(wù)復(fù)雜后,維護(hù)難度線性上升(已很不錯)。
2.2 DDD最大特點(diǎn)
DDD革命性在于:領(lǐng)域模型準(zhǔn)確反映了業(yè)務(wù)語言,而傳統(tǒng)微服務(wù)數(shù)據(jù)對象除了簡單setter/getter方法外,沒有任何業(yè)務(wù)方法,即失血模型,那么DDD領(lǐng)域模型就是充血模型(業(yè)務(wù)方法定義在實(shí)體對象中)。
三、落地
3.1 領(lǐng)域模型設(shè)計(jì)
以渠道中心(一個微服務(wù))作為例子來做領(lǐng)域模型設(shè)計(jì),核心就是設(shè)計(jì)2個圖,一個是戰(zhàn)略設(shè)計(jì)圖(宏觀) ,一個是戰(zhàn)術(shù)設(shè)計(jì)圖(細(xì)節(jié))。
1.領(lǐng)域戰(zhàn)略設(shè)計(jì)圖
戰(zhàn)略設(shè)計(jì)圖是從一個限界上下文的角度出發(fā)去分析業(yè)務(wù)場景。主要是宏觀上的核心域、子域、實(shí)體關(guān)系圖。demo如下圖:

2.領(lǐng)域戰(zhàn)術(shù)設(shè)計(jì)圖
戰(zhàn)術(shù)設(shè)計(jì)圖是從一個限界上下文的角度出發(fā)去分析業(yè)務(wù)場景。細(xì)化到核心業(yè)務(wù)字段、領(lǐng)域?qū)嶓w、值對象、領(lǐng)域服務(wù)、領(lǐng)域事件等等?;旧线@個圖畫完,代碼已經(jīng)知道怎么寫了。demo如下圖:

3.2 技術(shù)實(shí)現(xiàn)
整體項(xiàng)目框架分層圖如下所示:

如上圖,4層典型DDD分層結(jié)構(gòu),
1.展現(xiàn)層:controller層。無業(yè)務(wù)邏輯
2.應(yīng)用服務(wù)層:此層可以包含查詢邏輯,但核心業(yè)務(wù)邏輯必須下沉到領(lǐng)域?qū)印?/p>
3.領(lǐng)域服務(wù)層:業(yè)務(wù)在這里組裝。倉儲(資源庫)接口在此層定義。
4.基礎(chǔ)設(shè)施層:倉儲(資源庫)實(shí)現(xiàn)層+PO持久化層。
注:
1.簡單查詢不涉及業(yè)務(wù),是可以直接從應(yīng)用層直接穿透到PO查詢,不需要經(jīng)過domain層。如下圖所示,DDD本身是不限制非業(yè)務(wù)類操作跨層調(diào)用的。

2.DTO是不能存在于domain層的,DDD設(shè)計(jì)不認(rèn)為DTO是業(yè)務(wù)對象,entity才是。或者傳值簡單數(shù)據(jù)類型也是可以的。
3.2.1 服務(wù)調(diào)用問題
1.域內(nèi)調(diào)用
領(lǐng)域內(nèi)調(diào)用,隨便調(diào)用,絲般順滑。至于實(shí)現(xiàn),可以由一個核心域的倉儲實(shí)現(xiàn)層(第四層)去實(shí)現(xiàn)多個Repository接口。(比如這里A是核心域的實(shí)體名,B是支撐域、通用域等)

2.跨域調(diào)用

跨域分為
1.同上下文跨域:ACL層->Adapter適配器層→調(diào)用其它域的repository。--->不得已才使用,不推薦使用。
-
推薦:1.使用領(lǐng)域事件 eventbus來做解耦(nest-eventbus使用)
2.考慮是否有可能合并為一個領(lǐng)域. 2.跨上下文(肯定跨域):ACL層->Adapter適配器層->feign調(diào)用
3.2.2 包結(jié)構(gòu)
包結(jié)構(gòu)如下:

展開包結(jié)構(gòu)如下:

展現(xiàn)層:Controller,僅做接口的入口定義和編排轉(zhuǎn)發(fā),不做任何的業(yè)務(wù)處理;
應(yīng)用服務(wù)層:application,負(fù)責(zé)接口參數(shù)DTO的簡單校驗(yàn),以及DTO和實(shí)體值對象的數(shù)據(jù)轉(zhuǎn)換,對于簡單的業(yè)務(wù),也可以在應(yīng)用層加載實(shí)體直接執(zhí)行實(shí)體行為方法;
領(lǐng)域?qū)樱?/strong>
- 模型:根據(jù)領(lǐng)域模型分析領(lǐng)域內(nèi)各實(shí)體、聚合、聚合根、值對象等,這些對象在*.domain.model定義,實(shí)體內(nèi)的行為方法只負(fù)責(zé)維護(hù)實(shí)體自身的生命周期和狀態(tài);
- 行為:領(lǐng)域內(nèi)各實(shí)體、聚合、聚合根等,會有相應(yīng)的行為,在*.domain.model包下定義行為方法;
- 領(lǐng)域服務(wù):領(lǐng)域提供的接口服務(wù),需要定義在*.domain.service包下,業(yè)務(wù)相關(guān)的前置業(yè)務(wù)判斷、多個實(shí)體或值對象的行為邏輯處理等,都在領(lǐng)域服務(wù)中實(shí)現(xiàn),需要注意的是并不是每個實(shí)體都有一個對應(yīng)的領(lǐng)域服務(wù),但是依賴多個實(shí)體的行為方法,最好根據(jù)這個業(yè)務(wù)模塊是建立一個領(lǐng)域服務(wù);
- 倉儲:領(lǐng)域服務(wù)或上層應(yīng)用服務(wù)需要使用到的基礎(chǔ)設(shè)施層,包括DB、Feign調(diào)用等,定義在.domain.repository下,在.infrastructure.repository下實(shí)現(xiàn);
適配層:在acl包下的feign定義依賴外部的接口,并在acl的adapter包編寫轉(zhuǎn)換,由倉儲層操作實(shí)體時調(diào)用;
持久層:與常用DAO定義一致,由倉儲層操作實(shí)體時調(diào)用。