中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

領域驅動模型VO、DTO、DO、PO有什么區別

發布時間:2021-10-18 10:46:05 來源:億速云 閱讀:204 作者:小新 欄目:開發技術

這篇文章主要為大家展示了“領域驅動模型VO、DTO、DO、PO有什么區別”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“領域驅動模型VO、DTO、DO、PO有什么區別”這篇文章吧。

領域模型中的實體類

領域模型中的實體類分為四種模型:VO、DTO、DO、PO,各種實體類用于不同業務層次間的交互,并會在層次內實現實體類之間的轉化。

業務分層為:視圖層(VIEW+ACTION)、服務層(SERVICE)、持久層(DAO),相應各層間實體的傳遞如下:

領域驅動模型VO、DTO、DO、PO有什么區別

VO (View Object)視圖對象

用于展示層,它的作用是把某個指定頁面(或組件)的所有數據封裝起來。

DTO(Data Transfer Object)數據傳輸對象

這個概念來源于J2EE的設計模式,原來的目的是為了EJB的分布式應用提供粗粒度的數據實體,以減少分布式調用的次數,從而提高分布式調用的性能和降低網絡負載,但是這里,主要用于展示層與服務層之間的數據傳輸對象。

比如一張表有100個字段,那么對應的DTO就有100個屬性(大多數情況下,DTO內的數據來自多張表)。但是view層只需要顯示10個字段,沒有必要把整個PO對象傳遞到  client,這時我們就可以用只有這10個屬性的DTO來傳輸數據到 client,這樣也不會暴露 server  端的表結構。到達客戶端后,如果用這個對象來對應界面展示,那么此時它的身份就轉為 VO。

DO(Domain Object)領域對象

就是從現實世界中抽象出來的有形或無形的業務實體。

PO(Persistent Object):持久化對象

它跟持久層(通常是關系型數據庫)的數據結構形成一一對應的映射關系,如果持久層是關系型數據庫,那么,數據表中的每個字段就對應PO的一個屬性。

對于以上概念的理解,可能還不能形成一種抽象化思維,我們通過一個時序圖建立模型來描述上述對象在三層架構應用中的位置:

領域驅動模型VO、DTO、DO、PO有什么區別
  • 用戶提交請求(可能是填寫表單),表單上的數據在展示層被匹配為 VO。

  • 服務層把 VO 轉換為服務層對應方法所要求的 DTO,傳送給服務層。

  • 服務層首先根據 DTO 的數據構造一個 DO (或重建),調用 DO 的業務方法完成具體業務。

  • 服務層把 DO 轉換為持久層對應的 PO(一般使用 ORM 工具),調用持久層的持久化方法,把 PO 傳遞給它,完成持久化操作。

對于一個逆向操作,如讀取數據,也是用類似的方式轉換和傳遞。

VO 與 DTO 對比

VO 與 DTO 的區別

在這里我們可能會問:既然 DTO 是展示層與服務層之間傳遞數據的對象,為什么還要一個 VO 呢?

是的,對于絕大部分的應用場景來說,DTO 和 VO 的屬性值基本是一致的,而且他們通常都是  POJO,因此沒必要多此一舉。但不要忘記這是實現層的思維,對于設計層面來說,概念上還是應該存在 VO 和 DTO,因此兩者有著本質的區別,DTO  代表服務層需要接收的數據和返回的數據,而 VO 代表展示層需要顯示的數據。

用一個例子來說明可能會比較容易理解:

例如:Service 層有一個 getUser 的方法返回一個系統用戶,其中有一個屬性是 gender(性別),對于 Service  層來說,它只從語義上定義:1-男性、2-女性、0-未指定,而對于展示層來說,它可能需要用“帥哥”代表男性、“美女”代表女性、“秘密”代表未指定。

說到這里,可能你還會反駁,在服務層直接返回“帥哥、美女”不就行嗎?對于大部分應用來說,這不是問題,但設想以下,如果需求允許客戶可以定制風格,而不同的客戶端對于表現層的要求有所不同,那么,問題就來了。再者,回到設計層面分析,從職責單一原則來看,服務層只負責業務,與具體的表現形式無關,因此,它返回的  DTO,不應該出現與表現形式的耦合。

理論歸理論,這到底還是分析設計層面的思維,是否在具體實現層面必須這樣做呢?一刀切的做法往往會得不償失,下面我們具體分析應用中如何做出正確的選擇。

VO 與 DTO 的應用

在上面只是用了一個簡單的例子來說 VO 與 DTO 在概念上區別,這里我們具體分析在應用中如何做出正確的選擇。

在以下場景中,我們可以考慮把 VO 與 DTO 合二為一(注意:是實現層面):

  • 當需求非常清晰穩定,而且客戶端很明確只有一個的時候,沒有必要把 VO 和 DTO 區分開來,這時候 VO 可以隱退,用一個 DTO 即可,為什么是 VO  隱退而不是 DTO ?回到設計層面,服務層的職責依然不應該與展示層耦合,所以對于前面的例子,你很容易理解,DTO 對于  “性別”來說,依然不能用“帥哥美女”,這個轉換應該依賴于頁面的腳本(如 JavaScript)或其他機制(JSTL、EL、CSS)。

  • 即使客戶端可以進行定制,或者存在多個不同的客戶端,如果客戶端能夠用某種技術(腳本或其他機制)實現轉換,同樣可以讓VO隱退。

以下場景需要優先考慮 VO、DTO 并存:

  • 因為某種技術原因,比如某個框架(如Flex)提供自動把 POJO 轉換為 UI 中某些 Field 時,可以考慮在實現層面定義出  VO,這個權衡完全取決于使用框架的自動轉換能力帶來的開發和維護效率提升與設計多一個VO所多做的事情帶來的開發和維護效率的下降之間的比對。

  • 如果頁面出現一個“大視圖”,而組成這個大視圖的所有數據需要調用多個服務,返回多個DTO來組裝(當然,這同樣可以通過服務層提供一次性返回一個大視圖的DTO來取代,但在服務層提供一個這樣的防腐是否合適,需要在設計層面進行權衡)。

DTO 與 DO 對比

DTO 與 DO 的區別

首先是概念上的區別,DTO 是展示層和服務層之間的數據傳輸對象(可以認為是兩者之間的協議),而 DO  是對現實世界各種業務角色的抽象,這就引出了兩者在數據上的區別。

例如:UserInfo 和 User ,對于一個 getUser 方法來說,本質上它永遠不應該返回用戶的密碼,因此 UserInfo 至少比 User  少一個 password 的數據。而在領域驅動設計中,DO不是簡單的POJO,它具有領域業務邏輯。

DTO 與 DO 的應用

從上面會反向問題:既然 getUser 方法返回的 UserInfo 不應該包含 password,那么就不應該存在 password  這個屬性定義,但是如果同時有一個 createUser 的防腐,傳入的UserInfo需要包含用戶的 password,怎么辦?

在設計層面,展示層向服務層傳遞的 DTO 與 服務層返回給展示層的 DTO 在概念上是不同的,但在實現層面,我們通常很少會這樣做(定義兩個  UserInfo,甚至更多),因為這樣做并不見得很明智,我們完全可以設計一個完全兼容的DTO,在服務層接收數據的時候,不應該由展示層設置的屬性(如訂單的蹤跡應該由其單價、數量、折扣等決定),無論展示層是否設置,服務層都一概忽略,而在服務層返回數據時,不該返回的數據(如用戶密碼),就不設置對應的屬性。

對于DO來說,還有一點需要說明:為什么不在服務層中直接返回 DO 呢?這樣可以省去 DTO 的編碼和轉換工作,原因如下:

  • 兩者在本質上的區別可能導致彼此并不一一對應,一個DTO可能對應多個DO,反之亦然,甚至兩者存在多對多的關系;

  • DO 具有一些不應該讓展示層知道的數據;

  • DO 具有業務方法,如果直接把 DO 傳遞給展示層,展示層的代碼就可以繞過服務層直接調用它不應該訪問的操作,對于基于 AOP  攔截服務層來進行訪問控制的機制來說,這問題尤其突出,而在展示層調用DO的業務方法也會因為事物的問題,讓事物難以控制。

  • 對于某些ORM框架(如Hibernate)來說,通常會使用“延遲加載”技術,如果直接把 DO  暴露給展示層,對于大部分情況,展示層不在事物范圍之內(Open session in view  在大部分情況下不是一種值得推崇的設計),如果其嘗試在Session關閉的情況下獲取一個未加載的關聯對象,會出現運行時異常(對于Hibernate來說,就是  LazyInitliaztionException);

  • 從設計層面來說,展示層依賴于服務層,服務層依賴于領域層,如果把DO暴露出去,就會導致展示層直接依賴于領域層,這雖然依然單向依賴,但這種跨層依賴會導致不必要的耦合。

對于DTO來說,也有一點必須進行說明,就是DTO應該是一個“扁平的二維對象”舉個例子:

  • 如果User 會關聯若干個其他實體(例如 Address、Account、Region等),那么 getUser() 返回的  UserInfo,是否就需要把其關聯的對象的 DTO  都一并返回呢?如果這樣的話,必然導致數據傳輸量的大增,對于分布式應用來說,由于涉及數據在網絡上傳輸、序列化和反序列化,這種設計更不可接受。

  • 如果getUser除了要返回User的基本信息外,還需要返回一個AccountId、AccountName、RegionId、RegionName,那么,請把這些屬性定義到UserInfo中,把一個“立體”的對象樹“壓扁”成一個“扁平的二維對象”。

DO 與 PO 對比

DO 與 PO 的區別

DO 和 PO 在絕大部分情況下是一一對應的,PO是只含有 get/set 方法的POJO,但某些場景還是能反映出兩者在概念上存在本質區別:

  • DO 在某些場景下不需要進行顯式的持久化,例如利用策略模式設計的商品折扣策略,會衍生出折扣策略的接口和不同折扣策略實現類,這些折扣策略實現類可以算是  DO,但它們只會駐留在靜態內存池,不需要持久化到持久層,因此,這類 DO 是不存在對應的 PO的。

  • 同樣的道理,某些場景下,PO也沒有對應的DO,例如老師Teacher和學生Student存在多對多的關系,在關系數據庫中,這種關系需要表現為一個中間表,也就對應有一個TeacherAndStudentPO的PO,但這個PO在業務領域沒有任何現實的意義,它完全不能與任何DO對應上。

這里要特別聲明,并不是所有多對多關系都沒有業務含義,這跟具體業務場景有關,例如:兩個PO之間的關系會影響具體業務,并且這種關系存在多種類型,那么這種多對多關系也應該表現為一個DO,又如:“角色”與“資源”之間存在多對多關系,而這種關系很明顯會表現為一個DO——“權限”。

某些情況下,為了某種持久化策略或者性能的考慮,一個PO可能對應多個DO,反之亦然。例如客戶Customer有其聯系信息Contacts,這里是兩個一對一關系的DO,但可能出于性能的考慮(極端情況,權作舉例),為了減少數據庫的連接查詢操作,把Customer和Contacts兩個DO數據合并到一張數據表中。反過來,如果一本圖書Book,有一個屬性是封面cover,但該屬性是一副圖片的二進制數據,而某些查詢操作不希望把cover一并加載,從而減輕磁盤IO開銷,同時假設ORM框架不支持屬性級別的延遲加載,那么就需要考慮把cover獨立到一張數據表中去,這樣就形成一個DO對應多個PO的情況。  PO的某些屬性值對于DO沒有任何意義,這些屬性值可能是為了解決某些持久化策略而存在的數據,例如為了實現“樂觀鎖”,PO存在一個version的屬性,這個version對于DO來說是沒有任何業務意義的,它不應該在DO中存在。同理,DO中也可能存在不需要持久化的屬性。

DO 與 PO 的應用

由于ORM框架的功能非常強大而大行其道,而且JavaEE也推出了JPA規范,現在的業務應用開發,基本上不需要區分DO與PO,PO完全可以通過JPA,Hibernate  Annotations/hbm隱藏在DO之中。雖然如此,但有些問題我們還必須注意:

  • 對于DO中不需要持久化的屬性,需要通過ORM顯式的聲明,如:在JPA中,可以利用@Transient聲明。

  • 對于PO中為了某種持久化策略而存在的屬性,例如version,由于DO、PO合并了,必須在DO中聲明,但由于這個屬性對DO是沒有任何業務意義的,需要讓該屬性對外隱藏起來,最常見的做法是把該屬性的get/set方法私有化,甚至不提供get/set方法。但對于Hibernate來說,這需要特別注意,由于Hibernate從數據庫讀取數據轉換為DO時,是利用反射機制先調用DO的空參數構造函數構造DO實例,然后再利用JavaBean的規范反射出set方法來為每個屬性設值,如果不顯式聲明set方法,或把set方法設置為private,都會導致Hibernate無法初始化DO,從而出現運行時異常,可行的做法是把屬性的set方法設置為protected。

  • 對于一個DO對應多個PO,或者一個PO對應多個DO的場景,以及屬性級別的延遲加載,Hibernate都提供了很好的支持,請參考Hibnate的相關資料。

以上是“領域驅動模型VO、DTO、DO、PO有什么區別”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

丹东市| 德江县| 黄骅市| 汝城县| 增城市| 闸北区| 疏勒县| 昂仁县| 潮安县| 满洲里市| 崇文区| 秭归县| 灵武市| 嘉兴市| 湄潭县| 新竹县| 若羌县| 且末县| 河曲县| 金溪县| 汉寿县| 神农架林区| 乐昌市| 珠海市| 中阳县| 根河市| 秀山| 浪卡子县| 丘北县| 于都县| 沧州市| 永平县| 明水县| 洛南县| 浙江省| 高清| 德钦县| 常州市| 杨浦区| 益阳市| 镇远县|