項目軟知識系列--分層領域模型

分層領域模型簡介

其他網址

04.分層領域模型使用解讀 - 個人文章 - SegmentFault 思否

摘自:《阿里巴巴Java開發手冊》

一、分層領域模型規約

說明
DO(Data Object) 與數據庫表結構一一對應,通過DAO層向上傳輸數據源對象。
DTO(Data Transfer Object) 數據傳輸對象,Service或Manager向外傳輸的對象。若作爲分佈式服務的參數或返回對象,通常要實現序列化接口。
BO(Business Object)

        業務對象。由Service層輸出的封裝業務邏輯的對象。

        此對象在實際使用中有不同的理解,有的團隊採用領域驅動設計,BO 含有屬性和方法(具體可參考領域驅動設計的相關圖書);有的團隊將 BO 當做 Service 返回給上層的 “專用 DTO” 使用;而有的團隊則當做 Service 層內保存中間信息數據的 “DTO” 或者上下文對象來使用(本文采用這種理解)。

        比如 BO 中可以保存中間狀態,放一些邏輯等,這些並不適合放在 DTO 中

AO(Application Object)

        應用對象。在Web層與Service層之間抽象的複用對象模型,很貼近展示層,複用度低。

        有些團隊會將前端查詢的屬性和保存的屬性幾乎一致的對象封裝爲 AO,如讀取用戶屬性傳給前端,用戶在前端編輯了用戶屬性後傳回後端。這種用法將 AO 用作 Param 和 VO 或 Param 和 DTO 的組合。

VO(View Object)

        顯示層對象,通常是Web向模板渲染引擎層傳輸的對象。

        通常控制層將其作爲JSON 返回給前端然後前端渲染,或者加載頁面模板在後端進行填充。

Query

        數據查詢對象,各層接收上層的查詢請求。注意超過2個參數的查詢封裝,禁止使用Map類來傳輸。

        Param 爲查詢參數對象,適用於各層,通常用作接受前端參數對象。Param 和 Query 的出現是爲了避免使用 Map 作爲接收參數的對象。

二、領域模型命名規約

1) 數據對象:xxxDo,xxx即爲數據表名。

2) 數據傳輸對象:xxxDto xxxBo,xxx爲業務領域相關的名稱。

3) 展示對象:xxxVo,xxx一般爲網頁名稱。

4) POJO是DO/DTO/BO/VO的統稱,禁止命名成xxxPOJO。

爲什麼要有分層領域模型?

        有的朋友查詢參數喜歡通過 Map 或者 JSONObject 來封裝。有些朋友可能會認爲這麼多模型沒有必要,因爲通常各層模型的屬性基本相同,而且各種類型的分層模型對象轉換非常麻煩。

        使用不同的分層領域模型能夠讓程序更加健壯、更容易拓展,可以降低系統各層的耦合度。

        分層模型的優勢只有在系統較大時才體現得更加明顯。設想一下如果我們不想定義 DTO 和 VO,直接將 DO 用到數據訪問層、服務層、控制層和外部訪問接口上。此時該表刪除或則修改一個字段,DO 必須同步修改,這種修改將會影響到各層,這並不符合高內聚低耦合的原則。通過定義不同的 DTO 可以控制對不同系統暴露不同的屬性,通過屬性映射還可以實現具體的字段名稱的隱藏。不同業務使用不同的模型,當一個業務發生變更需要修改字段時,不需要考慮對其它業務的影響,如果使用同一個對象則可能因爲 “不敢亂改” 而產生很多不優雅的兼容性行爲。

        如果我們不願意定義 Param 對象,使用 Map 來接收前端的參數,獲取時如果採用 JSON 反序列化,則可能出現上一節所講到的反序列化類型丟失問題。如果我們不使用 Query 對象而是 Map 對象來封裝 DAO 的參數,設置和獲取的 key 很可能因爲粗心導致設置和獲取時的 key 不一致而出現 BUG。

流程圖

查詢視圖

        前端或者其它服務將 Param 對象作爲參數傳給控制層或者對外服務接口,然後調用內部的服務類,服務類內部的中間數據和這些數據相關的邏輯可以封裝爲 BO ,比如根據 BO 多個屬性判斷是否符合某個條件。

        如果查詢數據則封裝爲 Query 對象作爲參數,如果需要查詢其它依賴,則可以封裝 Param 對象作爲參數去查詢。DAO 層一般插入和更新的參數對象使用 DO 或 Param, 查詢參數一般使用 Query,刪除參數一般使用 Param。

 返回視圖

        數據訪問層(DAO)通常將數據封裝爲 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 部門,然後…

你會不會崩潰?

建議如果需要在內部業務中寫對實體相關的邏輯,可以考慮封裝到工具類 / 幫助類中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章