領域驅動設計最近又火了。概念不斷被說起,可是相信對於像筆者同樣的不少開發者對於其如何應用都一頭霧水。前端
正如《實現領域驅動設計》中做者提到的不一樣公司的業務能力開發能力和成熟度不同,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後端開發同窗,提供一個模塊分層的框架供參考
無忌,我教你的還記得多少?」 「回太師傅,我只記得一大半」
「 那,如今呢?」 「已經剩下一小半了」
「那,如今呢?」 「我已經把全部的全忘記了!」
「好,你能夠上了…」