歡迎光臨
每天分享高質量文章

你的專案應該如何正確分層?

點選上方“芋道原始碼”,選擇“置頂公眾號”

技術文章第一時間送達!

原始碼精品專欄

 

來源:http://t.cn/RdrmI7i

  • 1.背景

  • 2.如何進行分層

    • 2.1阿裡規範

    • 2.2最佳化分層

  • 3.分層領域模型的轉換

  • 4.總結


1.背景

說起應用分層,大部分人都會認為這個不是很簡單嘛 就controller,service, mapper三層。看起來簡單,很多人其實並沒有把他們職責劃分開,在很多程式碼中,controller做的邏輯比service還多,service往往當成透傳了,這其實是很多人開發程式碼都沒有註意到的地方,反正功能也能用,至於放哪無所謂唄。這樣往往造成後面程式碼無法復用,層級關係混亂,對後續程式碼的維護非常麻煩。

的確在這些人眼中分層只是一個形式,前輩們的程式碼這麼寫的,其他專案程式碼這麼寫的,那麼我也這麼跟著寫。但是在真正的團隊開發中每個人的習慣都不同,寫出來的程式碼必然帶著自己的標簽,有的人習慣controller寫大量的業務邏輯,有的人習慣在service中之間呼叫遠端服務,這樣就導致了每個人的開發程式碼風格完全不同,後續其他人修改的時候,一看,我靠這個人寫的程式碼和我平常的習慣完全不同,修改的時候到底是按著自己以前的習慣改,還是跟著前輩們走,這又是個艱難的選擇,選擇一旦有偏差,你的後輩又維護你的程式碼的時候,恐怕就要罵人了。

所以一個好的應用分層需要具備以下幾點:

  • 方便後續程式碼進行維護擴充套件。

  • 分層的效果需要讓整個團隊都接受

  • 各個層職責邊界清晰

2.如何進行分層

2.1阿裡規範

在阿裡的編碼規範中約束的分層如下:

img

開放介面層:可直接封裝 Service 方法暴露成 RPC 介面;透過 Web 封裝成 http 介面;進行 閘道器安全控制、流量控制等。

終端顯示層:各個端的模板渲染並執行顯示的層。當前主要是 velocity 渲染,JS 渲染, JSP 渲染,移動端展示等。

Web 層:主要是對訪問控制進行轉發,各類基本引數校驗,或者不復用的業務簡單處理等。

Service 層:相對具體的業務邏輯服務層。

Manager 層:通用業務處理層,它有如下特徵:1. 對第三方平臺封裝的層,預處理傳回結果及轉化異常資訊;2. 對Service層通用能力的下沉,如快取方案、中介軟體通用處理;3. 與DAO層互動,對多個DAO的組合復用。

DAO 層:資料訪問層,與底層 MySQL、Oracle、Hbase 進行資料互動。

阿裡巴巴規約中的分層比較清晰簡單明瞭,但是描述得還是過於簡單了,以及service層和manager層有很多同學還是有點分不清楚之間的關係,就導致了很多專案中根本沒有Manager層的存在。下麵介紹一下具體業務中應該如何實現分層

2.2最佳化分層

從我們的業務開發中總結了一個較為的理想模型,這裡要先說明一下由於我們的rpc框架選用的是thrift可能會比其他的一些rpc框架例如dubbo會多出一層,作用和controller層類似

img

1.最上層controller和TService是我們阿裡分層規範裡面的第一層:輕業務邏輯,引數校驗,異常兜底。通常這種介面可以輕易更換介面型別,所以業務邏輯必須要輕,甚至不做具體邏輯。

2.Service:業務層,復用性較低,這裡推薦每一個controller方法都得對應一個service,不要把業務編排放在controller中去做,為什麼呢?如果我們把業務編排放在controller層去做的話,如果以後我們要接入thrift,我們這裡又需要把業務編排在做一次,這樣會導致我們每接入一個入口層這個程式碼都得重新複製一份如下圖所示:

img

這樣大量的重覆工作必定會導致我們開發效率下降,所以我們需要把業務編排邏輯都得放進service中去做:

img

3.Mannager:可復用邏輯層。這裡的Mannager可以是單個服務的,比如我們的cache,mq等等,當然也可以是複合的,當你需要呼叫多個Mannager的時候,這個可以合為一個Mannager,比如邏輯上的連表查詢等。如果是httpMannager或rpcMannager需要在這一層做一些資料轉換

4.DAO:資料庫訪問層。主要負責“運算元據庫的某張表,對映到某個java物件”,dao應該只允許自己的Service訪問,其他Service要訪問我的資料必須透過對應的Service。

3.分層領域模型的轉換

在阿裡巴巴編碼規約中列舉了下麵幾個領域模型規約:

  • 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類來傳輸。

層次 領域模型
Controller/TService VO/DTO
Service/Manager AO/BO
DAO DO

每一個層基本都自己對應的領域模型,這樣就導致了有些人過於追求每一層都是用自己的領域模型,這樣就導致了一個物件可能會出現3次甚至4次轉換在一次請求中,當傳回的時候同樣也會出現3-4次轉換,這樣有可能一次完整的請求-傳回會出現很多次物件轉換。如果在開發中真的按照這麼來,恐怕就別寫其他的了,一天就光寫這個重覆無用的邏輯算了吧。

所以我們得採取一個折中的方案:

1.允許Service/Manager可以運算元據領域模型,對於這個層級來說,本來自己做的工作也是做的是業務邏輯處理和資料組裝。

2.Controller/TService層的領域模型不允許傳入DAO層,這樣就不符合職責劃分了。

3.同理,不允許DAO層的資料傳入到Controller/TService.

img

4.總結

總的來說業務分層對於程式碼規範是比較重要,決定著以後的程式碼是否可復用,是否職責清晰,邊界清晰。

當然這種分層其實見仁見智, 團隊中的所有人的分層習慣也不同,所以很難權衡出一個標準的準則,總的來說只要滿足職責邏輯清晰,後續維護容易,就是好的分層。

最後,如果你的團隊有更好的分層,或者上面所描述的有什麼錯誤的地方還請留言指正一下。



如果你對 Dubbo / Netty 等等原始碼與原理感興趣,歡迎加入我的知識星球一起交流。長按下方二維碼噢


目前在知識星球更新了《Dubbo 原始碼解析》目錄如下:

01. 除錯環境搭建
02. 專案結構一覽
03. 配置 Configuration
04. 核心流程一覽

05. 拓展機制 SPI

06. 執行緒池

07. 服務暴露 Export

08. 服務取用 Refer

09. 註冊中心 Registry

10. 動態編譯 Compile

11. 動態代理 Proxy

12. 服務呼叫 Invoke

13. 呼叫特性 

14. 過濾器 Filter

15. NIO 伺服器

16. P2P 伺服器

17. HTTP 伺服器

18. 序列化 Serialization

19. 叢集容錯 Cluster

20. 優雅停機

21. 日誌適配

22. 狀態檢查

23. 監控中心 Monitor

24. 管理中心 Admin

25. 運維命令 QOS

26. 鏈路追蹤 Tracing

… 一共 69+ 篇

目前在知識星球更新了《Netty 原始碼解析》目錄如下:

01. 除錯環境搭建
02. NIO 基礎
03. Netty 簡介
04. 啟動 Bootstrap

05. 事件輪詢 EventLoop

06. 通道管道 ChannelPipeline

07. 通道 Channel

08. 位元組緩衝區 ByteBuf

09. 通道處理器 ChannelHandler

10. 編解碼 Codec

11. 工具類 Util

… 一共 61+ 篇


目前在知識星球更新了《資料庫物體設計》目錄如下:


01. 商品模組
02. 交易模組
03. 營銷模組
04. 公用模組

… 一共 17+ 篇


目前在知識星球更新了《Spring 原始碼解析》目錄如下:


01. 除錯環境搭建
02. IoC Resource 定位
03. IoC BeanDefinition 載入

04. IoC BeanDefinition 註冊

05. IoC Bean 獲取

06. IoC Bean 生命週期

… 一共 35+ 篇

贊(0)

分享創造快樂