Java項目分層-以Onemall電商開源項目為例(轉)


add by zhj: 正在開發Java項目,Java比Python是復雜的多,比如結構化的參數要定義為bean,所以有DTO, BO, VO,很繁瑣。

如果項目是前后端分離的,建議只用DTO和BO就可以了

 

原文:http://www.iocoder.cn/Onemall/Application-layer/  「芋道源碼」

1. 概述

本文,我們來分享下 Onemall 電商開源項目的后端的應用分層規范。

目前,Onemall 的電商后端,采用 Spring Boot + Dubbo 的方式,提供給前端接口,也就是說,采用前后端分離的方式。為什么要提這一點呢,我們往下來瞅瞅。

2. 阿里巴巴規范

在說 Onemall 的應用分層規范之前,我們先來看看阿里巴巴分享的應用分層規范。

如下內容,引用自 《阿里巴巴Java開發手冊(詳盡版)》 。

考慮到排版,下面內容就不使用引用先。

  1. 【推薦】圖中默認上層依賴於下層,箭頭關系表示可直接依賴,如:開放接口層可以依賴於Web層,也可以直接依賴於Service層,依此類推:

 

 

  • 開放接口層:可直接封裝Service方法暴露成RPC接口;通過Web封裝成http接口;進行網關安全控制、流量控制等。
  • 終端顯示層:各個端的模板渲染並執行顯示的層。當前主要是velocity渲染,JS渲染,JSP渲染,移動端展示等。
  • Web層:主要是對訪問控制進行轉發,各類基本參數校驗,或者不復用的業務簡單處理等。
  • Service層:相對具體的業務邏輯服務層。
  • Manager層:通用業務處理層,它有如下特征: - 1) 對第三方平台封裝的層,預處理返回結果及轉化異常信息; - 2) 對Service層通用能力的下沉,如緩存方案、中間件通用處理; - 3) 與DAO層交互,對多個DAO的組合復用。
  • DAO層:數據訪問層,與底層MySQL、Oracle、Hbase等進行數據交互。
  • 外部接口或第三方平台:包括其它部門RPC開放接口,基礎平台,其它公司的HTTP接口。
  1. 【參考】(分層異常處理規約)在DAO層,產生的異常類型有很多,無法用細粒度的異常進行catch,使用catch(Exception e)方式,並throw new DAOException(e),不需要打印日志,因為日志在Manager/Service層一定需要捕獲並打印到日志文件中去,如果同台服務器再打日志,浪費性能和存儲。在Service層出現異常時,必須記錄出錯日志到磁盤,盡可能帶上參數信息,相當於保護案發現場。如果Manager層與Service同機部署,日志方式與DAO層處理一致,如果是單獨部署,則采用與Service一致的處理方式。Web層絕不應該繼續往上拋異常,因為已經處於頂層,如果意識到這個異常將導致頁面無法正常渲染,那么就應該跳轉到友好錯誤頁面,加上用戶容易理解的錯誤提示信息。開放接口層要將異常處理成錯誤碼和錯誤信息方式返回。

  2. 【參考】分層領域模型規約:

  • DO(Data Object):與數據庫表結構一一對應,通過DAO層向上傳輸數據源對象。
  • DTO(Data Transfer Object):數據傳輸對象,Service或Manager向外傳輸的對象。
  • BO(Business Object):業務對象。由Service層輸出的封裝業務邏輯的對象。
  • AO(Application Object):應用對象。在Web層與Service層之間抽象的復用對象模型,極為貼近展示層,復用度不高。
  • VO(View Object):顯示層對象,通常是Web向模板渲染引擎層傳輸的對象。
  • Query:數據查詢對象,各層接收上層的查詢請求。注意超過2個參數的查詢封裝,禁止使用Map類來傳輸。

3. Onemall 的選擇

看到阿里巴巴的規范之后,胖友是不是一臉懵逼,竟然有這么多 POJO ?!每個公司的業務復雜度不同,架構不同,所以 POJO 的選擇實際會有不同。當然,艿艿覺得,原則上是 Service 不將 DO 數據庫實體從 Service 暴露到 Controller ,避免后續數據庫設計的變化,影響暴露出去的方法。

🔥 如下圖,是 Onemall 的應用分層選擇:

 

 

我們按照自下而上,來看看各層的選擇。

  • 按照 Controller、Service、DAO 分成三層,去掉 Manager 層。
    • 大多數業務場景下,無需與第三方平台對接。
    • 當然,如果需要和第三方對接,還是會封裝成 Client ,例如說 Pay Client 和 第三方支付平台的對接。😈 所以實際還是有“隱藏”的 Manager 層。
  • DAO 層
    • 入參,使用 DO(Data Object)。
    • 出參,使用 DO(Data Object)。
  • Service 層
    • 入參,使用 DTO(Data Transfer Object)。
      • 需要加上 Bean Validation 注解,從而校驗參數。
      • 需要加上 Swagger API 注解,因為后續 Controller 很大可能性會使用到它,從而生成 API 文檔。更細的原因,我們在 Controller 層一起講。
      • 示例:AdminAddDTO 和 AdminUpdateDTO 。
    • 出參,使用 BO(Business Object)。
      • 本來考慮使用 DTO ,考慮到區分,所以使用了 BO 。😈 當然,我了解到蠻多朋友返回是使用 DTO 的,問題不大。
      • 需要加上 Swagger API 注解,原因同 DTO 。
      • 示例:AdminBO 。
  • Controller 層
    • 入參,使用 DTO(Data Transfer Object)。
      • 因為前后端分離之后,Controller 大多數情況下,基本是將 Service 進行封裝,提供 API 接口。所以大多數情況,Service DTO 可以重用,所以就默許使用 Service DTO 。😈 當然,這塊有不同意見的胖友,可以一起來討論下,我也挺糾結的。
      • 當然,如果 Service DTO 不夠用的情況下,可以自己在創建下 Controller DTO 。
      • 本來想 Controller 單獨在取個 XXO 的名字,結果想了半天沒想出來,就繼續沿用 Service DTO 了。
      • 所以,因為是這樣的設定,我們就要求 Service DTO 上,增加 Swagger API 注解。
    • 出參,使用 BO(Business Object)。
      • 原因,也是同 Controller 入參。
      • 當然,如果 Service BO 不夠用的情況下,可以自己在創建下 Controller VO 。

艿艿:每個公司的分層架構不同,歡迎一起討論。妥妥的。

因為分層規范是后來調整的,所以項目中可能有部分不符合這樣的規范,具體以示例為主。

🔥 聊完了應用分層的話題,我們在一起討論下 Service 邏輯異常的時候,如何進行返回。這里的邏輯異常,我們指的是,例如說用戶名已經存在,商品庫存不足等。一般來說,常用的方案選擇,有兩種:

  • 封裝統一的業務異常類 ServiceException ,里面有錯誤碼和錯誤提示,然后進行 throws 拋出。
  • 封裝通用的返回類 CommonResult ,里面有錯誤碼和錯誤提示,然后進行 return 返回。

一開始,我們選擇了 CommonResult ,結果發現如下情況:

  • 因為 Spring @Transactional 聲明式事務,是基於異常進行回滾的,如果使用 CommonResult 返回,則事務回滾會非常麻煩。
  • 當調用別的方法時,如果別人返回的是 CommonResult 對象,還需要不斷的進行判斷,寫起來挺麻煩的。

所以,后來我們采用了拋出業務異常 ServiceException 的方式。

🔥 可能機智的小伙伴會問,如果拋出異常,Controller 如何做通用的處理,答案在 GlobalExceptionHandler 類。結果 Spring MVC 的 Exception 處理機制,我們會將 ServiceException 轉換成 CommonResult 對象,返回給前端。

當然,故事還沒有結束,Controller 雖然返回的是 BO / VO 對象,我們選擇在外面包了一層 CommonResult ,用於返回可能存在的業務邏輯錯誤的情況。因為呢,HTTP API 是語言無關,無法使用 Java Excpetion 。

不過哈,最初我們使用了 @ControllerAdvice 機制,自動全局將 BO / VO 對象,包裝成 CommonResult 對象,但是和基友 didi 討論了下這個選擇,建議還是 Controller 顯示聲明 CommonResult 返回,考慮點是 AOP 不應該破壞方法的 Schema ,即有一天去掉這個 AOP ,依然返回的是 CommonResult 。

4. 彩蛋

🔥 最后的最后,大家總是會討論到的一個問題,這么多 POJO 對象,如何進行復制呢?Onemall 采用 mapstruct ,因為廣告法的原因,我們不能說它是最好用的,但是的確是(並且,效果還非常非常非常的高),哈哈哈哈。具體的示例,可以看看 AdminConvert 。


免責聲明!

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



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