Web編程規范之三層架構設計規范


本篇是我對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那里只要看到滿意的結果就好。

Service分層設計

Service分層設計是什么意思呢? 讓我們來看一個例子

public interface OrderService{
    /** 查詢一個訂單. */
    OrderBO findOne(String orderId);

    /** 取消訂單. */
    void cancel(OrderBO orderBO);
}

在上述的代碼中OrderService包含根據orderId獲取訂單這個操作。那么對於買家而言調用此方法是需要對買家進行安全性驗證的,非該用戶的訂單則不能返回給調用方。為了代碼的整潔性我們衍生出來一個BuyerService,這個BuyerService主要處理和買家相關的業務,對買家進行驗證后在進行調用相應的Service。

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":"錯誤的信息" } 
  • 統一返回對象工具:該工具主要是封裝一些快捷返回【統一返回的對象】的方法,避免大量冗余的代碼。
  • 全局的異常處理攔截器:雖然上面有了【統一返回的對象】,但是當拋出異常的時候,返回給用戶的信息破壞了這個對象的結構,導致用戶處理起來困難。所以需要全局的異常攔截器,攔截異常然后處理成【統一返回的對象】格式。
  • 統一異常類型和異常代碼:如果在代碼中存在各式各樣的異常類型和異常信息的硬編碼,一經修改就有可能導致遺漏或邏輯報錯等問題,讓代碼難以維護。所以需要統一的異常類型和集中管理異常代碼是必要的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM