為了賬號安全,請及時綁定郵箱和手機立即綁定

代碼分層后的新世界

2019.10.25 15:22 7953瀏覽

大家好,我是文賀,一名工作五年的一線 Java 開發,主要擅長 Java 源碼、DDD(領域驅動設計)、業務中臺框架的落地,本次我們分享的主題名稱叫做:代碼分層后的新世界,其核心思想就是和大家一起討論下目前比較通用的分層架構,討論一下其由來、優點和簡單的代碼落地實踐。

2.1 代碼不分層,遲早要崩潰。

14 年參加工作的時候,寫了一年的 MVC 架構,大概的后端工程結構(我們說的是后端工程結構)如下:
圖片描述
整個依賴關系是:Controller -> Service -> Model,調用關系也是如此。

使用這種架構,當初我天天做的事情主要是:

  1. 復制粘貼 Service;
  2. 復制粘貼 DAO;
  3. 復制粘貼轉化 DTO、VO、DO。

最痛苦的是每次新來需求時,我都是新建一個 Service 接口和類,然后開始復制粘貼,當時我在想:這樣寫出的代碼真的面向對象么?天天這樣寫代碼有什么意義??!我自己又能得到什么成長??!

15 年的時候,幸運的一次機會,我接觸到了 DDD,學習了分層框架,從哪開始之后,我再也沒有用過 MVC 框架。

當然我們不是說 MVC 框架不好,只是對業務理解不深,對缺少建模指導方法論的新同學來說,MVC 中的 Service 層的定義太模糊了,Service 里面放了各種亂七八糟的東西,比如有消息隊列、分布式緩存這種底層技術框架,比如有流程編排的業務邏輯,比如有計算、校驗、創建、落庫、發消息等復雜動作,可以說 Service 就是一個大雜燴,只要有需求要寫代碼,我們統統寫到 Service 層中,用亂七八糟的 package 進行分層,到項目上線之后,代碼看都不想看,其他同事來看代碼時,也是很懵,只有一個感覺,Service 層太亂了!

這其實就是 MVC 框架最大的問題:Service 層無結構化,無工程化。

大家可以仔細思考一下,自己在做的項目有沒有這種感覺,你覺得你現在的代碼邏輯清晰么?你有沒有被寫代碼的前任坑過?你有沒有經歷過代碼亂到你都不敢動的沮喪?

如果你有這種感覺的話,那么恭喜你,你就是本篇文章的最適合閱讀人群。

2.2 我應該分層么?

接下來大家在內心中來問自己幾個問題:

  1. 你想不想寫出面向對象的代碼?
  2. 在未來 1 年內,你是否還會繼續呆在公司?來維護這套代碼
  3. 公司的業務是不是在飛速發展中?
  4. 公司的業務是不是稍微有點點復雜?

如果以上四個維度你都回答的是:我覺得你可能可以嘗試一下分層架構。

從你個人成長上來說,在你在以后的開發生涯中,分層架構你肯定會遇到的,提前嘗試對你是有好處的。

隨著公司業務的發展擴張,合理的分層結構更容易達到業務的高內聚低耦合,工程更易維護,更易擴展。

當你非常熟悉分層技巧時,分層并不會增加你的工作量,當然在你初次嘗試時,由于不熟悉分層代碼的結構和落地,肯定會有一些障礙的,但當你跨過了障礙,得到的一定是分層之后的新世界。

2.3 比 MVC 更優秀的分層架構。

我們說的分層架構,主要是分成五層,當然網上有四層、六層的,這都沒有關系,四、五、六層雖然最終分的層數不同,但核心思想都是一樣的,首先我們一起來看一下下面這張圖(請你畫一分鐘從上而下把圖看一遍,先只看,不求理解):

圖片描述

  1. 這張圖代表著一個系統的整體工程結構;

  2. 這種圖從上而下,一共分為五層,分別為:Controller 入口層、App 應用層、Domain 領域層、Spi 層、Infrastructure 基礎設施層,網上四層架構是少了 Spi 層,六層架構是多了 Api 層;

  3. 五層之間的依賴關系是:Controller -> App -> Domain;Spi -> Infrastructure;Infrastructure -> Domain,從依賴關系上看,只有 App 層依賴 Domain 層,Domain 從不會去依賴其他層,這樣設計目的是為了讓 Domain 層比較穩定,圖中層和層之間的箭頭表示模塊之間的依賴關系;

  4. 五層之間的調用關系是:Controller -> App -> Domain -> Spi -> Infrastructure,調用關系就是從上而下,比較正常。

  5. 每一層里面白色長方形標識著自己該干的事情,也就是每層的職責所在。

我們先不深入理解,先整體比較一下這種圖和之前 MVC 的圖的不同之處:

  1. 很明顯的看出,MVC 后端是三層:Controller 層、Model 層、Service 層,而五層架構多了兩層。
  2. MVC 中的 Controller 層對應五層架構中的 Controller 層,Model 層部分對應五層架構中的 infrastructure 層, Service 層對應五層架構中的 App 層、Domain 層和 Spi 層。

雖然我們現在還不是很清楚五層架構每一層是什么含義,但我們已經明顯看到 MVC 中的 Service 層被拆分成了三層,這點是整體上來看最大的不同。

2.4 五層架構的介紹

2.4.1 五層架構的基本介紹

五層架構分為五層,在寫代碼時,就會有五個模塊,每一層對應著一個模塊,下圖演示的是一個 Maven 工程:

圖片描述
這樣在寫代碼之前,我們首先要思考的是:我的代碼應該寫在那一層?

接著我們就一層一層的來介紹,因為我們只有知道了每層的職責,才會清楚在每一層中應該寫什么樣的代碼。

2.4.2 Controller 層介紹

Controller 層,也有的同學叫做 Web 層,中文翻譯為入口層,比 MVC 中的 Controller 層功能更加豐富。

五層架構中的 controller 主要做三件事情:

  1. 啟動項目;

我們現在的項目基本都是使用的 SpringBoot 架構,在項目啟動時,一行代碼即可搞定,比如:SpringApplication.run(啟動類.class),那么這行啟動 Spring 容器的代碼,我們就寫在 Controller 層。

  1. 協議轉化;
  2. 接口轉化。

2、3 我們統一舉一個例子,大家應該買過理財吧,比如我在京東金融上去買理財,理財的頁面如下:

圖片描述
聲明:圖片來自京東金融官網,如有侵權,請聯系作者,立馬刪除。

從圖片中我們可以看到,京東金融上的理財產品非常多,而且從頁面上我們就可以清晰的看到,這些理財產品的創建者并不都是京東金融,而是來自平安保險、建信基金等等保險基金平臺,也就是說京東金融把其他保險基金平臺的理財產品放到自己的平臺上來賣。

要實現這個目的,京東金融就要對接這些基金保險公司,假設京東金融向外統一的接口是 A,接口定義 10 個字段,協議規定是必須使用京東自己封裝的 SKD。

比如現在我們需要對接平安保險,平安保險也有自己的一套對接標準,對外的接口是 B,接口定義 9 個字段,協議規定是 Https。

假設現在由京東金融來適配平臺保險,那么適配工作主要是兩個:

  1. 協議轉化,京東金融需要開發 Https 的功能,來適配平安保險的協議;
  2. 由于兩家公司的接口出入參的標準都不同,所以京東金融需要做接口轉化。

舉這個例子,其實就是想說明,如果你有協議轉化,接口轉化的場景,按照分層職責,代碼你可以寫在 controller 層。

2.4.3 App 層介紹

App 是英文 Application 的縮寫,中文叫做:應用層,主要干一件事情:流程編排。

流程編排的意思是:按照一定的業務場景(流程),將各種領域行為(SpringBean)按照一定的順序事先編排好,由流程引擎客戶端統一執行。

按照這個定義,大家想一想,你平時工作中有用到流程編排么?

有!你肯定有用到,我們平時把 SpringBean 之間的調用邏輯在 App 里面寫死,也是一種寫死的流程編排,比如這樣:

圖片描述
當然更好的是,你在頁面上拖拽一下 SpringBean,執行業務邏輯時,就能按照你事先編排的業務邏輯進行執行,做到頁面可視可配置的地步。

除了最低的編排功能外,流程引擎還能做:事務開啟,組件同步/執行執行等等功能。

所以我們小結一下,App 層的職責:

  1. 流程編排。
  2. 事務控制。

除了以上兩種事情,其余業務邏輯放進 App 層都是耍流氓。

PS:我們在《面試官系統精講Java源碼及大廠真題》中三個小節中都用流程引擎串 ThreadLocal、ConcurrentHashMap、ThreadPollExecutor、Runnable 等源碼的知識點,感興趣的同學可以關注下。

2.4.4 Domain 層介紹

Domain 層我們叫做領域層,是寫業務邏輯的模塊,也是領域模型落地的模塊,Domain 層的職責如下:

  1. Domain 層只寫核心的業務邏輯,非核心的業務邏輯可以放到 Spi 中;
  2. App 層直接依賴 Domain 層,這個很好理解,因為代碼會從 App 層直接調用到 Domain 層;
  3. Domain 層從不依賴其他層,Domain 的下游模塊會反向依賴 Domain 層。

Domain 層和上下游的依賴關系如下圖:
圖片描述

這時候有的同學可能會問,如果 Domain 層要查詢數據庫(數據庫是在 Infrastructure 層的),Domain 層不依賴 Infrastructure 層,是如何查詢得到呢?

好問題,答案是:在 Domain 層定義一個接口,Infrastructure 層去實現,在 Domain 層直接依賴接口即可把實現注入,代碼如下:

圖片描述
這種模塊之間反向依賴的方式,在 DDD(領域驅動設計)中叫做依賴倒置。

接著我們就要來描述 Domain 層和 MVC 中的 Service 層的最大不同了,DDD 將 Service 的代碼按照職責進行了細化拆分,拆分成了:實體、聚合、領域服務、應用服務、倉儲、工廠等等,當然你不了解這些定義沒有關系,你只需要知道按照這樣拆分后,最直接的效果是:每個同學在寫代碼時,再也不簡單的新建 Serivce 復制粘貼了,而是會想現在新增的代碼是實體么?是聚合么?是領域服務么?

這些領域概念背后都對應著各自的寫法規范,讓你從此拋棄復制粘貼的漩渦,寫出面向對象的代碼,至于 Domain 層各個概念之間的關系,我們不會細說(因為比較復雜,內容很多),有興趣的同學可以看看下面這種圖,也可以訪問我的博客觀看:

圖片描述

2.4.6 Infrastructure 介紹

Infrastructure 中文翻譯為基礎設施層,主要放一些技術框架的,比如說消息隊列中間件、分布式緩存框架、流程引擎、規則引擎、Mysql 數據庫等等,我們不在 Infrastructure 層寫業務代碼,只會放一些和技術有關系的技術框架,這一層相對來說比較容易理解。

2.4.5 Spi 層介紹

SPI 層的由來

SPI 是我們在實踐平臺化的過程中提出來的。

在一些大型的平臺化項目中,我們發現基礎設施層會承擔很多東西,比如說倉儲的實現,有的倉儲實現的邏輯是非常復雜的,還比如說我們還會在基礎設施層里面調用下游的接口,在里面寫一些模型適配的邏輯,慢慢的我們就會覺得別扭,別扭的點如下:

1:我們在基礎設施層寫的邏輯代碼算業務么?

2:基礎設施層不應該放技術框架類的東西么,如消息隊列框架,遠程調用框架等等,不是應該給整個系統提供技術支持么,為什么要寫一些倉儲實現,模型適配的業務邏輯呢?

我們討論了很久,最后的結論是:寫在基礎設施層里面的邏輯也算業務,雖然不是我們核心模型的業務邏輯,但其穩定性和準確性依然會對核心模型產生關鍵影響。

于是我們單獨拆出一個模塊來:SPI,一開始取得英文全稱:Serial Peripheral Interface,我們把放到基礎設施層的代碼遷移到了 SPI 層。

在一個基金保險平臺項目中,我們甚至成立了一個 SPI 小組,大概 4~8 個人的樣子,專門負責對接各種三方基金和保險。

于是也就演變成了我們現在的分層架構,SPI 層也會寫一些業務,比如說模型適配,倉儲實現,基礎設施層就比較干凈,都是一些技術框架。

SPI 層的定義

對于 SPI 的定義,我喜歡這么描述:Domain 和下游溝通的橋梁。

SPI 層目前主要負責三大功能:

1:實現 Domain 提出的要求,比如說 Domain 層定義的倉儲接口,SPI 層來實現;

2:適配下游三方提出的要求,我們和下游進行對接時,并不是所有的字段 Domain 層都需要關心,對于 Domain 不關心的字段,需要 SPI 自己去填充,定制適配下游的要求;

3:溝通串聯 Domain 和下游,使兩者可以正確穩定的通信。

SPI 做的事情其實還是滿多的,既要滿足 Domain 層,又要滿足下游,同時還保證 Domain 和下游之間通信的準確和穩定。

2.5 分層帶來的好處

好處很多,我們簡單列舉幾個:

  1. 整體系統的模塊分層清晰,有形成一定的規范,易于維護;
  2. 每一層都可以集中注意力干自己該干的事情;
  3. 實現了 Domain 層的高內聚低耦合;
  4. 代碼更加面向對象,只要你寫過一次這樣的代碼,你絕對會喜歡上的。

2.6 Show Code

也許很多同學已經聽上面的內容很困了,甚至都想睡著了,那么請同學們醒一醒,再堅持一下,和我們一起最后來看下分層代碼的分層寫法。

首先模塊分層我們已經清楚了,接著我們分成一層一層的看下去,我們直接在圖上面進行文字描述,這樣更容易講解和觀看。

2.6.1 Controller

圖片描述

2.6.1 App

圖片描述

2.6.1 Domain

圖片描述

2.6.1 Spi

圖片描述

2.6.1 Infrastructure

圖片描述
以上截圖其實只是展示出 DDD 工程的分層技巧,除了這些,我們對各個領域概念都有指定工程的規范,只要你熟悉我們的工程規范,看代碼就完全了解我們整體的領域模型,完全可以了解業務(能做到的同學很少,但只要能做到,升職加薪是必然)。

到這里,不知道同學們發現沒有,其實這篇文章就是從工程分層的角度出發,給大家展示了一下 DDD 領域驅動設計的魅力,DDD 是一種方法論,如果你苦于:

  1. 拿到需求文檔后,不知道如何分析需求,如何建領域模型;
  2. 不知道如何畫領域模型圖,UML 圖;
  3. 不知道如何建數據模型(建表);
  4. 不知道如何驗證領域、數據模型的正確性;
  5. 不知道如何寫出高內聚低耦合的業務代碼;
  6. 不知道如何劃分業務、系統、模塊之間的邊界;
  7. 關于業務的一切思考。

那么你都可以來學習 DDD,學習 DDD 其實不需要太多基礎(有導師帶著的話),只需要一顆能靜下來的心,

歡迎訪問我的博客

點擊查看更多內容

本文原創發布于慕課網 ,轉載請注明出處,謝謝合作

57人點贊

若覺得本文不錯,就分享一下吧!

評論

相關文章推薦

正在加載中
意見反饋 幫助中心 APP下載
官方微信

舉報

0/150
提交
取消
开奖号码