可落地的DDD的(2)-為什麼說MVC工程架構已經過時

方丈的寺院發表於2019-05-21

摘要

mvc是一種軟體設計模式,最早由Trygve Reenskaug在1978年提出,他有效的解決了表示層,控制器層,邏輯層的程式碼混合在一起的問題,很好的做到了職責分離。但是在實際的編碼實踐過程中,你會發現這個模式隨著業務的擴充套件,變的邏輯混亂,程式碼重合度很高。這裡提出借鑑DDD思想的一種新的工程結構

mvc的問題

通常一個前後端分離的系統,後端工程系統結構圖通常下面這樣
在這裡插入圖片描述

  1. 四層 controller/service/manager/mapper

  2. 不可以同級呼叫

  3. 上級可以知曉下級,下級不可知曉上級,也就是bean的轉化放在上級

這個分層結構職責分離是按照縱向切分的

  1. 資源服務層repository是面向DB程式設計

  2. service層是面向前端頁面程式設計。

也就是說,對於某一塊的業務,他沒有將邏輯抽象到一起,他只是將一次request按照縱向切分了。沒有進行橫向的業務切分。
這樣將會導致的問題
職責分散,邏輯重複度高

  • bean的建立太隨意,基本就是一個需求對應一些dto, vo,query bean
  • 不同開發者對於同一個領域的東西有不同的bean,同一個開發者對於相同邏輯的bean,過了幾個月,又定義出一個差不多的bean

沒有邊界

  • 根本沒有上下文/邊界的概念,比如說店鋪會和使用者有互動,訂單會和使用者有互動,通常在DB儲存時只會存關聯id,然後需要去取對應的名稱,其他屬性資訊。這些資訊的獲取,有些開發在manager層操作,然後將屬性定義到了店鋪相關的DTO中;有些放在了service層做。controller/service/manager各個層次都可以呼叫,沒有任何約束。

mvc的演進

按照上述的說明,在一個單體服務中,隨著業務的不斷迭代,可能會發生什麼嚴重的問題。

舉幾個真實鮮活的例子

分庫分表的例子
實體A是我們業務中的一個基礎的重要的實體,對應的資料表tableA,一開始業務很簡單,只有1個服務,在這個服務裡面呼叫。後來業務擴張了,有十幾個服務了,然後十幾個服務直接查這個tableA。tableA也擴張成為了tableA,tableB,tableC。有些人覺得程式碼重複度高了,將mapper/manager層拆成共通的部分打成一個jar包,然後各個微服務中引入這個jar。業務變得更加複雜了,服務擴充套件到幾十個了,tableA資料也有幾千萬了,這時候要做分庫分表了,怎麼整。

最後花了差不多1年,涉及十幾個團隊,才把這個mapper/manager呼叫改掉,然後做分庫分表。

有人可能覺得這個只要在服務拆分時,避免直接呼叫就可以了,那再舉個其他型別的例子。

使用者等級的例子

使用者的等級,使用者的分級是很複雜的,不同的業務階段有這個不同的定義。比如一開始定義一個欄位叫grade的代表使用者等級。
然後各個業務都在查這個表的欄位grade進行判斷,然後產品需要改了,增加了判斷必須同時要達到什麼條件才能稱作等級x。這時候你又得滿世界的改了。

DDD的工程架構

那如何運用DDD的思想進行改造呢
核心思想:封裝領域內的邏輯,統一對外暴露的入口,防止業務邏輯洩露。

在這裡插入圖片描述

  • 在mvc縱向切分的基礎上,增加一層領域的橫向切分
  • 同一個工程裡面,領域之間的呼叫只能通過domainService,這樣可以遮蔽領域內的資料庫是如何持久化的,業務邏輯是如何判斷的、演算法是如何實現的。
    service之間可以直接呼叫。
  • 領域內還是縱向切分,安裝mvc分層結構。

上面的只是一個草圖,我們真實的結構圖比這要稍微複雜些。領域內會區分領域物件,領域服務,基礎設施層。這樣在領域內進行指責分離,不過從實際的執行過程中領域內的比較細節,執行起來ROI比較低,推薦大家可以先按這套執行。

畫外音:估計有些程式設計師看到這個工程結構變化呵呵一笑,覺得沒多大價值,沒什麼改變必要。

這種工程的結構劃分從提出來的到真正被我們團隊成員接受的時間週期差不多是8個月。
原因大概是這麼幾類

  1. 引入新的分層,太複雜了,增加了程式碼複雜度
  2. 我這塊業務很簡單,CRUD就行了,沒涉及到服務之間的互動。直接mvc一條道走到黑就可以。

如果你看這篇文章也是這種感受,不妨花點時間看下你們業務的程式碼,看看重複度有多高,看看邏輯有多散亂。你就會明白。

DDD工程的演進

DDD工程的演進也就是服務的拆分了,放到下期講。

總結

很多DDD的文章都在說傳統的程式設計方式是面試資料庫程式設計,導致物件中只有getter,setter,也就是貧血模型,貧血模型是沒有業務邏輯,程式導向設計,不符合物件導向設計原則。

對於這個結論我是同意的,但是對於造成的原因不是很同意。個人認為造成這個原因的主要原因還是在於長期以來的MVC這種模式只有縱向切分導致。如果結合橫向切分,有沒有DDD也無所謂。這裡再引用一下驅動方法不能改變任何事情這段話,如果你能深入理解職責、封裝。並隨著業務的迭代,不斷的重構你的程式碼,那麼你不需要什麼DDD,或者其他方法論。

使用職責、封裝和組合;
以介面的視角思考,即“人們如何使用我的元件?”;
使用相關技術寫好程式碼,包括可讀性、資訊性、簡潔、自描述,儘量避免顯式地使用模式;
有能力回答特定業務的“本質”;“本質”是一個模型,但不意味著類和方法,它意味著回答問題“這個業務如何真正地工作?”

因為這些約束,都是強迫你去思考,去做職責的思考,去做模組的封裝。如果你/你團隊成員已經領會其中的道理並很好的運用,還需要這些條條框框幹嗎呢?

下一篇領域與微服務劃分,欲知後事如何,請聽下回分解。

相關閱讀

https://www.cnblogs.com/stoneFang/p/10888630.html

關注【方丈的寺院】,第一時間收到文章的更新,與方丈一起開始技術修行之路
在這裡插入圖片描述

相關文章