本篇是我對Web開發規範中關於三層架構設計規範的一些淺見。雖然三層架構是比較普通,也比較簡單的架構設計模式。可是隨着業務的增加,涉及到的對象愈來愈多,處理的邏輯愈來愈複雜。這其中不免會出現設計不當,從而致使業務報錯或邏輯代碼混亂等問題的出現。下面我就來簡單的談一談我是如何設計的?(注:本篇的看法是在基於當前比較流行的MVC設計模式,不知道什麼是MVC設計模式的小夥伴,請自行查找相關資料。)數據庫
邏輯劃分
在三層的架構設計中一般會採用以下設計方案對邏輯進行劃分:設計模式
Controller (表現層)
在MVC設計模式中使用Controller表明,它的主要做用爲:安全
- 針對用戶的輸入進行驗證
- 構建對象並調用 Service
- 返回給用戶VO或VM
Service(業務層,也叫服務層)
- 接收Controller傳過來的對象
- 編寫業務邏輯
在編寫業務的時候有幾個技巧: 1)對於全部更新操做,都把BO對象當成參數來接收,把更新後的對象當返回值架構
public OrderBO cancel(OrderBO orderBO) { return orderBO; } /** 取消訂單. */ public OrderBO finish(OrderBO orderBO) { return orderBO; } /** 完成訂單. */ public OrderBO paid(OrderBO orderBO) { return orderBO; } /** 支付訂單. */
2)對於查詢而言,若是對象不存在,直接返回null或拋出異常都可。當返回爲null時,上層調用須要進行判null處理 3) 若是是更新類操做,被操做的對象不存在時,這個時候必定要拋出異常 4)在須要拋出異常的地方使用日誌詳細的記錄 5)若是在Service的方法中涉及到多個更新操做,須要在Service中開啓事務。即便該方法的上層調用Service開啓了事務,該方法也應該開啓。以建立訂單爲例框架
@Transactional public OrderBO save(OrderBO orderBO) { // ..... OrderMaster orderMaster = new OrderMaster(); BeanUtils.copyProperties(orderBO, orderMaster); // 存儲訂單 orderMasterRepository.save(orderMaster); productInfoService.decrStock( orderBO.getOrderDetailList().stream() .map(e-> new StockBO(e.getProductId(),e.getProductQuantity())) .collect(Collectors.toList()) ); return orderBO; } @Transactional //即便上層調用有這個標記,這裏最好也加,由於可能會單獨調用 public void decrStock(List<StockBO> stockBOList) { stockBOList.stream().forEach(e -> { ProductInfo productInfo = this.findOne(e.getProductId()); if (productInfo == null) { throw new WxbpException(ErrorCodeEnum.PRODUCT_NOT_EXIST); } Integer remainStock = productInfo.getProductStock() - e.getProductStock(); if (remainStock < 0) { throw new WxbpException(ErrorCodeEnum.PRODUCT_STOCK_ERROR); } productInfo.setProductStock(remainStock); repository.save(productInfo); }); }
- 調用其餘Service或Repository
- 返回BO對象或POJO對象
Repository(數據訪問層)
- 由業務層調用和數據庫進行交互來實現對數據的CRUD操做。爲了提升效率,這通常會使用ORM框架,例如Mybatis或Hiberinate。
- 返回POJO對象
模型對象定義
除基本的架構外,還會存在不少其餘的實體對象,這些對象包括:工具
- POJO: 普通的Java對象,映射數據表,內部屬性均對應於數據表的列名。用於接收數據庫的返回結果。一般在Service和Repository之間進行數據傳遞。
- BO : 業務對象,是針對整個邏輯單元構成的對象,例如訂單業務對象不只包含訂單的信息,還包含訂單明細。該對象主要在Controller和Service 之間進行數據傳遞,能夠由不一樣的POJO組成。若是POJO的屬性與BO相同時,能夠不用建立BO對象而直接使用POJO對象來充當BO,同理其餘模型對象也如此。
- VO : 視圖對象,這類對象僅用於返回用戶能夠看到的信息。例如用戶請求產品信息時,返回了一個產品業務對象,咱們不能直接把這個對象返回給用戶,由於對象的內容包含了不少敏感信息(庫存,採購價等),須要進一步對其處理,過濾掉這些敏感信息,從而構成了視圖對象。
- VM:視圖模型,和VO很像,也是用戶返回用戶能夠看到的信息。這個概念是MVC設計模式中的,對應於MVC中的M,這個對象一般對應於整個頁面的信息,由一個或多個VO。例如我的中心的頁面,不只包含用戶信息,還有訂單信息,積分的信息等。
- PO:參數對象,這個對象映射到Controller方法中的參數。當Controller須要的參數過多時,咱們一般使用對象進行參數的接收,這個對象就是請求參數對象。
簡化理解
對於上述三層架構中涉及的業務和對象,咱們能夠簡化這樣理解,假設有這樣一個情景。 BOSS把總監(Controller)叫到辦公室 ,說:公司經營的情況如何了,給我出個整體報告我看看。總監收到指令後把A部門的經理(Service)叫到了辦公室,並說:我須要近一年A部門的總結報告。A部門經理須要收到請求後,開始使用報表工具作事(Repository),當遇到了本身處理不了的地方又會叫其餘有關聯的經理幫助處理(調用其餘的Service),最後完成了整件事。把報告交給了總監,總監看過以後發現沒什麼問題,又把B部門的經理叫到了辦公室,並說了需求(有時候須要給B看A部門的報告的部分或全部結果), B部門的經理收到請求後,也是同A同樣的處理辦法,把報告交給了總監。此時總監這裏已經有了BOSS要的全部數據,而後進行彙總,交給BOSS。而在這個過程當中,爲保密和方便各方之間的溝通,BOSS和總監之間的對話能夠理解成 PO -> BO 的過程,總監和經理的對話能夠理解成BO->POJO的過程,固然有些經理對業務理解比較透徹就不須要轉換了(直接使用BO,此時BO=POJO)。而這個對話過程反過來就是,進行逐層彙報的時候,應該言簡意賅,一語中的,至於你怎麼作的,就不要向上層反映了。最後達到BOSS那裏只要看到滿意的結果就好。this
##Service分層設計 Service分層設計是什麼意思呢? 讓咱們來看一個例子編碼
public interface OrderService{ /** 查詢一個訂單. */ OrderBO findOne(String orderId); /** 取消訂單. */ void cancel(OrderBO orderBO); }
在上述的代碼中OrderService包含根據orderId獲取訂單這個操做。那麼對於買家而言調用此方法是須要對買家進行安全性驗證的,非該用戶的訂單則不能返回給調用方。爲了代碼的整潔性咱們衍生出來一個BuyerService,這個BuyerService主要處理和買家相關的業務,對買家進行驗證後在進行調用相應的Service。spa
public class BuyerService{ public OrderBO checkOrderOwner(String userId,String orderId){ OrderBO orderBO = orderService.findOne(orderId); if(!orderBO.getUserID().equals(userId)) { //拋出異常; } return orderBO; } /** 查詢用戶的訂單列表. */ public OrderBO findOrderList(String userId,String orderId){ OrderBo orderBO =checkOrderOwner(userId,orderId); return orderBO } }
這個分層理解起來也和三層架構相似,你固然能夠在Controller中直接使用 OrderService 進行操做,就像你也能夠直接使用Repository操做同樣,只不過是比較麻煩而已。更好的設計有助於提升代碼的整潔性,可讀性和可維護性。架構設計
提高效率小妙招
如上的設計中涉及複雜業務的同時又衍生出了不少不一樣種類的對象,在開發的過程當中會不免會形成代碼的可讀性,安全性和可維護性下降,下面咱們看一些能夠提高總體的開發效率的小妙招。
- 對象轉換工具:實現不一樣對象間的轉換操做,能夠由一個對象拆分爲多個對象,也能夠由多個對象組合成一個對象。
- 統一返回的對象:這一點對調用方而言很重要,若是咱們的返回值結構多種多樣,這會讓調用方不知如何處理,從而增長使用難度。故須要統一的返回值結構,一般返回值的JSON結構以下:
{ "code":"0","msg":"操做成功","data":"須要返回給用戶的數據"} 或 { "code":"錯誤的狀態碼","msg":"錯誤的信息" }
- 統一返回對象工具:該工具主要是封裝一些快捷返回【統一返回的對象】的方法,避免大量冗餘的代碼。
- 全局的異常處理攔截器:雖然上面有了【統一返回的對象】,可是當拋出異常的時候,返回給用戶的信息破壞了這個對象的結構,致使用戶處理起來困難。因此須要全局的異常攔截器,攔截異常而後處理成【統一返回的對象】格式。
- 統一異常類型和異常代碼:若是在代碼中存在各式各樣的異常類型和異常信息的硬編碼,一經修改就有可能致使遺漏或邏輯報錯等問題,讓代碼難以維護。因此須要統一的異常類型和集中管理異常代碼是必要的。