1.前言
《手冊》關於分層模型部分的規約以下:前端
【參考】分層領域模型規約
DO (Data Object): 此對象與數據庫表結構一一對應,經過 DAO 層向上傳輸數據源對象。數據庫
DTO (Data Transfer Object): 數據傳輸對象,Service 或 Manager 向外傳輸的對象。後端
BO (Business Object): 業務對象,由 Service 層輸出的封裝業務邏輯的對象。數據結構
AO (Application Object): 應用對象,在 Web 層與 Service 層之間抽象的複用對象模型,極爲貼 近展現層,複用度不高。分佈式
VO (View Object): 顯示層對象,一般是 Web 向模板渲染引擎層傳輸的對象。Query: 數據查詢對象,各層接收上層的查詢請求。函數
注意超過 2 個參數的查詢封裝,禁止使用 Map 類來傳輸。工具
那麼咱們須要思考如下幾個問題:學習
爲何須要這些分層領域模型?
實際開發中每種分層領域模型都會用到嗎?
本小節咱們將重點分析和解答這些問題。spa
2.分層模型
2.1 常見的分層模型有哪些?含義是什麼?
學習和工做常常會接觸到分層領域模型,如 DO、BO、DTO、VO 等。其中 DO、BO、DTO、AO、Query 在《手冊》給出了一些解釋,這裏給出一些補充。設計
DTO (Data Transger Object) 爲數據傳輸對象,一般將底層的數據聚合傳給外部系統,它一般用做 Service 和 Manager 層向上層返回的對象。須要注意的是:若是做爲分佈式服務的參數或返回對象,一般要實現序列化接口。
Param 爲查詢參數對象,適用於各層,一般用做接受前端參數對象。Param 和 Query 的出現是爲了不使用 Map 做爲接收參數的對象。
BO (Bussiness Object) 即業務對象。該對象中一般包含業務邏輯。此對象在實際使用中有不一樣的理解,有的團隊採用領域驅動設計,BO 含有屬性和方法(具體可參考領域驅動設計的相關圖書);有的團隊將 BO 當作 Service 返回給上層的 「專用 DTO」 使用;而有的團隊則當作 Service 層內保存中間信息數據的 「DTO」 或者上下文對象來使用(本文采用這種理解)。
好比 BO 中能夠保存中間狀態,放一些邏輯等,這些並不適合放在 DTO 中:
@Data
public class ItemBO {
private Boolean isOnSell; private Boolean hasStock; private Boolean hasSensitiveWords; public Boolean isLegal() { if (isOnSell == null || hasStock == null || hasSensitiveWords == null) { return false; } return isOnSell && hasStock && (!hasSensitiveWords); }
}
VO (View Object) 爲視圖對象,一般做爲控制層經過 JSON 返回給前端而後前端渲染或者加載頁面模板在後端進行填充。
AO (Application Object) 應用對象。一般用在控制層和服務層之間。有些團隊會將前端查詢的屬性和保存的屬性幾乎一致的對象封裝爲 AO,如讀取用戶屬性傳給前端,用戶在前端編輯了用戶屬性後傳回後端。這種用法將 AO 用做 Param 和 VO 或 Param 和 DTO 的組合。
2.2 爲何要有分層領域模型?
還有的朋友查詢參數喜歡經過 Map 或者 JSONObject 來封裝。有些朋友可能會認爲這麼多模型沒有必要,由於一般各層模型的屬性基本相同,並且各類類型的分層模型對象轉換很是麻煩。
使用不一樣的分層領域模型可以讓程序更加健壯、更容易拓展,能夠下降系統各層的耦合度。
分層模型的優點只有在系統較大時才體現得更加明顯。設想一下若是咱們不想定義 DTO 和 VO,直接將 DO 用到數據訪問層、服務層、控制層和外部訪問接口上。此時該表刪除或則修改一個字段,DO 必須同步修改,這種修改將會影響到各層,這並不符合高內聚低耦合的原則。經過定義不一樣的 DTO 能夠控制對不一樣系統暴露不一樣的屬性,經過屬性映射還能夠實現具體的字段名稱的隱藏。不一樣業務使用不一樣的模型,當一個業務發生變動須要修改字段時,不須要考慮對其它業務的影響,若是使用同一個對象則可能由於 「不敢亂改」 而產生不少不優雅的兼容性行爲。
若是咱們不肯意定義 Param 對象,使用 Map 來接收前端的參數,獲取時若是採用 JSON 反序列化,則可能出現上一節所講到的反序列化類型丟失問題。若是咱們不使用 Query 對象而是 Map 對象來封裝 DAO 的參數,設置和獲取的 key 極可能由於粗心致使設置和獲取時的 key 不一致而出現 BUG。
3.開發中的應用
講完了概念和優點,你們可能會認爲文字描述有些抽象,接下來經過查詢和返回兩個視角爲你們展現實際項目中的一種常見的用法(貧血模型)。
3.1 查詢視圖
咱們先從請求訪問的視角去了解不一樣分層數據模型在實際項目中一種常見用法。
圖片描述
前端或者其它服務將 Param 對象做爲參數傳給控制層或者對外服務接口,而後調用內部的服務類,服務類內部的中間數據和這些數據相關的邏輯能夠封裝爲 BO ,好比根據 BO 多個屬性判斷是否符合某個條件。
若是查詢數據則封裝爲 Query 對象做爲參數,若是須要查詢其它依賴,則能夠封裝 Param 對象做爲參數去查詢。DAO 層通常插入和更新的參數對象使用 DO 或 Param, 查詢參數通常使用 Query,刪除參數通常使用 Param。
3.2 返回視圖
接下來咱們從數據返回的視角去了解分層領域模型在實際項目中的一種常見用法:
圖片描述
數據訪問層一般將數據封裝爲 DO 對象傳給 Service 層,Manager 或 Client 層每每將查詢結果封裝爲 DTO 傳給 Service 層。
一般內部服務層經過 DTO 往外傳輸數據。Controller 一般將 DTO 組裝爲前端須要的 VO 或者直接將 DTO 外傳 。
RPC 服務接口將 DTO 直接返回或者從新封裝爲新的 DTO 返回給外部服務。
另外即便同一個接口,可是一個對內使用,一個對外暴露,儘可能使用不一樣接口,定義不一樣的參數和返回值,從而避免由於修改內部或外部的數據結構而致使另一個受到影響,這也是單一職責原則的要求。
單一職責原則:一個類應該有且只有一個改變的理由。
也有部分團隊 RPC 的請求和響應參數都經過 DTO 來承載,經過 XXRequestDTO 和 XXResponseDTO 來表示。
實踐分層領域模型可以提升項目的健壯性、可拓展性和可維護性,下降了系統內部各層的耦合度。
上面只是給出一種參考,不少團隊對部分分層模型的理解會有差別,實際的使用過程當中根據本身團隊的規模能夠適當變通。好比有不少團隊項目並非特別大,爲了下降複雜度,只用到了 DTO 、VO 、DO 三種分層領域模型。
最後對分層領域模型的規約這裏進行補充:
【參考】不提倡在 DTO 中寫邏輯,強制不要在 RPC 返回對象的 DTO 中封裝邏輯。
有些團隊的個別成員會將根據成員屬性做判斷的一些函數寫到 DTO 中,最奇葩的是該邏輯還主要供內部系統業務層使用。
如:
public class xxDTO{
// 各類屬性
// 邏輯代碼
public static boolean canXXX(){
// 各類判斷
}
}
這樣形成系統的耦合性很是強。
若是對方用到了這個函數,將來此函數的內部邏輯必須發生變化,未必能及時通知對方升級,容易形成 BUG。
即便耗費了成本找到了使用方,爲了你的功能,讓別人被迫升級版本從新上線也是很是不專業的事情。
顯然這樣作不合理。
試想一下今天 A 部門告訴你他們因某個功能被迫修改了某個 RPC 返回值 DTO 的某個方法,大家用到沒有?用到升級一下哈…
而後 B 部門的人明天告訴你一樣的話,而後 C 部門,而後…
你會不會崩潰?
建議若是須要在內部業務中寫對實體相關的邏輯,能夠考慮封裝到工具類 / 幫助類中。
4.總結
本節主要講分層模型的目的和優點以及在實際開發中的常見用法。給你們一個參考,讓你們可以在開發時知道哪些模型應該放到哪一層。
下一節將講述不一樣的分層領域模型之間的轉換的正確姿式。
5.思考題
在實際項目開發中,不一樣的分層領域模型之間一般須要轉換,你是如何轉換的?
參考資料阿里巴巴與 Java 社區開發者.《 Java 開發手冊 1.5.0:華山版》:2019:38