VO(DTO)模式在分層架構設計中是否需要的扯淡
Peter Wei
引子:
前兩天,在內部討論中。公司有一開發人員向我拋出問題:我們Web層和App應用程式層用DTO(VO)對象,沒有直接用PO,你有什麼好的建議?我自然知道他說這句話的意思,PO到DTO(VO)的不停轉換,太麻煩,增加太多工作量了。因為我是負責做架構的,他是想讓我向上面CTO反映取消掉DTO對象。但現有的架構是原先就有的,而且在一定程度上,我也認為需要用DTO對象。所以最終沒有全部取消。
概念掃盲
我們現在大多數的應用,我想都是基於分層架構的:
Web層(Struts2 or SpringMVC等)App應用程式層(Service)Dao層(ORM)DB
PO:也就是一般概念上的Domain Object,如hibernate 中的Entity.一般用於Service層--Dao層間的資料轉送。
DTO(VO):也就是一般意義上的VO,封裝後的對象。一般用於Web層—Service層間的資料轉送入。
VO(DTO) VS PO
引子中開發人員是想用PO透傳所有層。也就是共用PO,然後取消掉DTO。
1.PO透傳的程式碼範例:
比如有一個Order的hibernate entity.
我們假設Order下還有Account等好幾個對象和集合。
Java代碼
- public class OrderAction{
- private Order order=new Order();
- private List<Order> orderList
- public String add(){
- orderService.add(order);
- return xx;
- }
-
- public String query(){
- orderList=orderService.find(Object param);
- return xx;
- }
- }
-
- public class OrderService{
-
- public void add(Order){
- orderDao.saveOrUpdate(Order o);
- }
- public List find(Object param){
- return orderDao.find(Object param);
- }
- }
public class OrderAction{private Order order=new Order();private List<Order> orderListpublic String add(){orderService.add(order);return xx;}public String query(){orderList=orderService.find(Object param);return xx;}}public class OrderService{public void add(Order){orderDao.saveOrUpdate(Order o);}public List find(Object param){return orderDao.find(Object param);}}
2.用VO(DTO)模式的程式碼範例:
Java代碼
- public class OrderService{
-
- public void add(OrderVO vo){
- Order order=new Order();
- Account account=new Account();
-
- account.setXX(vo.getXX);
- //一堆XX
- order.setAccount(account);
- //又一堆XX
- orderDao.saveOrUpdate(Order);
- }
- public List<OrderVO> find(Object param){
- List list=orderDao.find(Object param){
- for(xxxx){
- //集合下的集合
- //更多的XX轉換
- setXX
- setXX
- }
- }
- }
- }
public class OrderService{public void add(OrderVO vo){Order order=new Order();Account account=new Account();account.setXX(vo.getXX);//一堆XXorder.setAccount(account);//又一堆XXorderDao.saveOrUpdate(Order);}public List<OrderVO> find(Object param){List list=orderDao.find(Object param){for(xxxx){//集合下的集合//更多的XX轉換setXXsetXX}}}}
PO的透傳的優點一目瞭然,就是不用進行大量的資料對象轉換,極大的減少開發人員的工作量。而且大多數PO和VO是重合的,屬性什麼都一樣。我們知道雖然有BeanUtils等工具進行自動轉換,但依然很繁瑣。
Java代碼
- BeanUtils.copyProperties(desc,src);
BeanUtils.copyProperties(desc,src);
為什麼用VO(DTO)
我在以前的工作過程中做過各種公司專屬應用程式還有網站應用程式。大多數的項目,我們都用的是PO透傳View,Action,Service,Dao層。但有兩個項目中沒用。
1.一個是聯通的某OSS系統,該系統的基本技術架構是這樣的:
Flex(Web層)BladeDSBusiness Facade(DTO)Service(Spring)Dao(hibernate)
我們一眼看出這是一個分布式的系統。也就是Flex和Java應用通迅的異構系統。在Flex和Java應用之間用DTO模式結合外觀Facde模式就順理成章,水道渠成了。為什麼呢?因為兩個進程間的通迅,複雜層級的Hibernate Domain Object不好直接傳到Flex端,因為是用remote協議傳輸。Hibernate的lazy特性以及序列化都是個問題。所以加一個外觀層進行PO到DTO的傳輸就很有必要了。
2.一個是一個電子商務網站,網站的基本技術架構是這樣的:
有兩部分,一部分是內部管理核心系統:
Flex應用Hessian Remote協議App Service(Sring)Dao(hibernate)
另一部分是電子商務系統,也就是對外的部分
Web應用(Jstl,jsp,SpringMVC)Hessian Remote協議 App Service(Sring)Dao(hibernate)
其中Flex應用單獨部署一個工程,Web應用單獨部署一個工程,App Service和Dao又部署一個工程。Flex和web應用都是單獨通過hessian協議訪問App應用。
我們來總結一下為什麼要用DTO:
1. JavaEE各層之間解耦,這是從設計角度來說的。也就是說Domain Object(PO)直接封死在Dao層。高內聚,低耦合是我們追求的一個目標。但由於在一般的應用中大量的PO,VO轉換,增加了工作量,也有些人說是過度設計。
2. 隔離變化。當在一些大型的應用情境以及Domain經常變化的系統裡,View層可以只關注DTO對象,不用關心Domain層PO對象,如hibernate entity的不斷變化。
3. 利於發揮個人技術特長,特別是按層來分工開發的團隊。如有人專註於Web層,只做SpringMVC和介面這部分。還有人專門做Spring和Hibernate部分。兩組的開發人員定義好介面資料就行,也就是DTO(VO)。我們當時做的電子商務網站就是這樣的。
4. 當系統發展大後,擴充成各種前端介面後,可以有效隔離核心應用程式層。如又有web介面,又有swing的cs介面,又有手機用戶端。
5. 當分層部署時,也就是Web層(jsp,Struts2)和App層(Spring,dao)在不同機器上時,用Remote協議通迅,DTO是必需的。如處理hibernate lazy load和序列化等問題。在電子商務應用中分層部署的主要好處是減少各層負載量,提高效能。
6. 在一些特定情境,如需要防火牆的地方,需要把核心資料層(Service,Dao,DB)放在防火牆內。
小結
綜合以上所述,我認為VO(DTO)模式還是必需的,特別是考慮到以後擴充性的問題。但是在我們的團隊中為了開發進度和避免對以前開發的功能影響,沒有強制要求在Action和Service層間一定用DTO.但是新開發的功能模組要求用DTO模式,算是一種折中吧。以後系統大了之後再重構好了。要不然以後怎麼有事做呢?哈哈。而且boss有時間壓力在那。畢竟現在還沒打算分層部署,夠用就行,先不過度設計了。
其它牛人的一些觀點:
參考:Martin Fowler的POEAA(公司專屬應用程式架構模式)中分布式模式中的Remote Facade模式和DTO模式。
Rod Johnson在J2EE without ejb中是反對用DTO的。
歡迎拍磚,謝絕漫罵!