-
粗略的看一遍領域驅動的理論,不需要做到每個名詞都能理解,知道領域驅動是什么,解決什么問題的,大概有哪幾個模塊即可。
-
找一個具體的項目(推薦阿里的cola4),了解定義了幾個module,每個module的作用是什么的,分別負責什么功能,了解每個module都依賴了其他哪些module,每個module之間是如何實現了相互調用的。
-
對照着項目中的module和領域驅動的理論,分析每個module分別對應理論中的哪塊功能,理解該module的存在意義是什么,為什么這樣設計,符合領域驅動中的哪個觀點。
領域驅動設計是一個軟件架構的設計理念,是一種設計思想,在代碼結構上,並沒有所謂的“標准”可言,只要符合領域驅動的設計理念即可。對於不同的業務系統可以設計出符合該理念、適合自己系統的框架結構。
首先理解一下常見的MVC和MVCS模型。MVC是我們說的比較多的一種模型,M-數據模型,V-數據展示,C-控制層。其中M層主要包括各種數據庫實體類(entity),DAO,Mapper等和數據的存儲獲取有關的類。V層在web系統中主要就是各種頁面,html、jsp、js、css等。而對於C的話就是常見的各種Controller,用來接收前端的各種增刪改查交互,同時還負責從M層獲取數據的包裝和適配,請求參數的有效性校驗,前端請求的各種跳轉和重定向等。
為了解決這個問題,可以引入MVCS模型,這個模型也就是我們日常中用的最多的模型。這里的S是指的業務服務層,把一些通用的功能方法提取到S層,用來為各個Controller提供服務。在實際的開發中,一般會為各個小模塊定義自己的MVCS,一個完整的模塊通常會包括:UserList.jsp、UserController、UserService、UseerDao。而多個小模塊組成了整個project。
上面的modules.goods、modules.system都是在同一個idea-module(idea的模塊)下,以目錄來做物理隔離的。有時候會根據情況把modules目錄下的功能模塊獨立出來,當做一個project下面的獨立idea-module來處理。
那么什么是領域驅動呢?在我的理解中就是把MVCS中的S層更加細粒度化,在“領域”的維度上進行拆分,實現每個領域的“高內聚低耦合”,以便達到領域之間的物理和邏輯的隔離。每個領域的業務高度內聚在一起,通過充血模型,實現自己的領域業務,同時暴露出接口供外部調用。
在傳統MVCS中的S層,通常會根據某個對象創建一個service,例如UserService、CompanyService。或者根據功能創建一個service,例如LoginService、MessageService。這些只是簡單的把一些可公用的方法簡單的在物理層面放在一個java文件中,實現了簡單的物理隔離。這種維度的拆分過於微觀,是在方法粒度上進行的隔離,雖然實現了方法的共享,但是在整個service中取糅合着各種各樣的功能。例如:定義一個OrderService,里面有訂單的添加、修改、刪除邏輯,還有訂單的結算邏輯。但從領域角度考慮,訂單的增刪改查屬於訂單管理的業務(領域),而訂單的結算屬於支付相關的業務(領域),兩個不同的業務邏輯代碼卻在相同的service中,這樣安排會顯得邏輯混亂。而按照領域驅動的思想,把同一領域的相關操作整合在同一domain下面,且只負責與該領域相關的操作,同時暴露出功能接口供外部調用。領域之間可以直接相互依賴聚合。當領域內部操作需要依賴其他服務(例如修改db數據)時,可以通過自定義接口方式滿足領域內部操作,然后由其他領域外部服務實現該接口。
下面就用一個具體的領域驅動框架cola4來詳細剖析下領域驅動的設計理念。
項目示例在:https://github.com/alibaba/COLA/tree/master/samples ,可以下載后直接導入idea。
導入后的結構如下:
分別對應cola中的五個模塊:
這里的五個idea-module分別是client、adapter、application(app)、domain、infrastructure,五個module的依賴關系如圖所示:
-
client不依賴項目中的其他模塊(圖中的cola組件不屬於項目中的模塊)
-
application依賴domain和client模塊,同時會通過domain間接依賴infrastructure
-
adapter直接依賴於application,同時通過application間接依賴client
-
domain不依賴任何其他模塊,其內部會在gateway中定義領域網關接口
-
infrastructure依賴於domain,在gatewayImpl中實現領域接口
其五個模塊的詳細功能如下:
-
adapter
-
適配層,一般是充當controller,對不同的請求方(頁面/RCP)提供服務。對於未前后端分離的web系統,可以把靜態頁面放在該module下面。該層主要依賴於client層。
-
adapter依賴於client,在controller接收到請求之后,需要調用定義在client中的interface執行后續流程。
-
controller中接收的入參和出參也是定義在client中
-
-
client(facade)
-
有些文章會把client叫做facade,其目的是用來暴露接口和定義傳遞數據的。在client里面會定義一些interface和DTO、BO、VO等,當框架支持CQRS的時候,也會把各種event放在該層中。
-
controller調用的接口都會定義在該層中,同時各種入參和出參也在該層定義。
-
client層不依賴其他任何模塊
-
-
application(facadeImpl)
-
application層實現了各種功能,供其他模塊調用,主要是adapter中的各種controller。
-
application實現了定義在client中的各種接口,而client接口中定義的方法就是暴露出供adapter層調用的功能。
-
application中會定義各種executor來實現各種功能的具體邏輯
-
executor在執行業務邏輯的時候,通常會調用domain進行業務處理,其依賴於domain模塊。如果是簡單的邏輯也會直接調用infrastructure層,例如一些簡單數據的存儲等
-
-
domains
-
領域層主要包括領域對象(domain)、領域服務(service)、和領域網關(gateway)。
-
領域對象entity不同於DO和DTO。DO(data object)是屬於數據層的對象和db表做一一對應;DTO(data transmission object)是在adapter層定義的數據傳輸對象,充當傳輸媒介。而領域對象定義在domain中,按照充血模型定義,在其內部實現各個領域的業務操作。
-
domain不依賴其他任何層,當需要調用其他模塊服務時,則根據依賴倒置原則,在gateway里面定義個接口,domain的業務層調用該接口的方法完成整個業務邏輯,而接口的實現則放在Infrastructure實現。gateway中的領域接口也可以直接供application層調用。
-
-
infrastructure
-
infrastructure為基礎服務層,主要處理和外部系統的交互。例如數據庫操作、redis操作、RPC調用等
-
gatewayImpl實現domain層的領域網關
-
支持各種config配置
-
infrastructure中需要對領域對象轉換為DO進行操作
-
下面就看下example中的add請求是怎么處理的。
首先在adapter中定義了一個MetricsController,該controller對外提供了addATAMetric方法,同時里面依賴了client中的com.alibaba.craftsman.api.MetricsServiceI接口。同時注意入參ATAMetricAddCmd,同樣定義在client中。
之后找到addATAMetric的實現類是在app中,如下圖所示。同時app中又依賴於其內部定義的各個executor完成對應的操作。
executor以組件形式定義在app中,同時調用domain中的領域接口,執行后續的業務操作。
定義在domain中的領域網關:
而領域網關的實現放在了infrastructure中:
infrastructure接收到定義在domain中的MetricItem領域對象后,首先轉換為MetricDO對象,然后調用metricMapper插入數據庫中。由於這里使用了CQRS,對數據的寫操作需要通知到讀模塊,因此這里發布了一個MetricItemCreatedEvent,通知讀模塊更新數據。
通過上面的方式,跟着controller提供的接口,順藤摸瓜一步一步跟蹤到infrastructure層,就會對整個cola項目結構有個清晰的理解,然后再結合着領域驅動的設計思想,大概就明白了各個層級和接口的設計目的。
參考文章:
https://www.bianchengquan.com/article/539687.html
https://blog.csdn.net/significantfrank/article/details/100074716
https://www.cnblogs.com/duanxz/p/9922170.html