怎麼説服領導,能讓我用DDD架構肝項目?

語言: CN / TW / HK

作者:小傅哥
博客:https://bugstack.cn
原文:https://mp.weixin.qq.com/s/ezd-6xkRiNfPH1lGwhLd8Q

沉澱、分享、成長,讓自己和他人都能有所收穫!😄

一、前言

領導:為什麼要使用DDD?

我也苦思冥想,怎麼跟領導説咱們從 MVC 升級到 DDD 吧,因為 DDD 代碼結構更加清晰、領域驅動比測試驅動開發更加先進、研發的兄弟們也更想用用新框架等。

不過這麼聊被噴一頓不説,還得説你是過度設計瞎折騰,咋回事呢?因為沒聊到重點呀,你MVC升級DDD;給業務帶來了什麼提升了交付效率嗎降低了公司研發成本嗎,都沒有?不僅沒有,你還説為了後期的迭代維護,前期會需要更多的設計和開發時間。咋?你是想這一個Q就把我送走嗎,我剛來咱們部門KPI在那懸着,壓的我頭髮都白了!別瞎搞,求穩!

那就不搞了嗎?搞哇,不讓搞換領導!但搞之前,要考慮清楚,DDD 不是 Silver Bullet,你有一腔熱血雖好,可是也得知曉 DDD 的設計原則是什麼、它更適合的場景是什麼、與 MVC 對比有什麼雲泥之別。

二、開發成本

使用 DDD 模式開發代碼的成本到底在哪?是因為使用 DDD 四層分層結構就比MVC 三層分層結構 更浪費時間嗎?其實並不是,因為四層結構相對於三層結構,反而更好的區分了代碼所屬職責,在熟悉模塊功能職責後,開發起來也會更加順暢。

那這裏的 DDD 領域驅動設計開發的成本在哪呢?這個成本在於對於一個複雜系統又尚未在開發前期就有非常充足的經驗來拆分職責邊界劃分功能領域明確編排邏輯對未知流程擴展的把控上,所帶來的風暴模型設計成本

而通常使用的 MVC 結構基本不會出現這樣的問題,因為在實際的代碼中,DAO、PO、VO等都是共用的,大家在開發代碼的時候,像堆泥球一樣面向過程寫代碼,直接串聯出產品的PRD功能節點即可,不用過多的思考解耦和內聚。

那不是可以設計模式嗎,這就需要看你是站在哪個維度去思考問題。設計模式在這裏是戰術問題的,DDD和MVC是確定戰略問題的,有點像是説:“方向不對,努力白費一樣”

那麼現在我們再來看這條開發成本曲線:

架構模式,開發成本曲線

  • 與其他兩種分層結構相對比,使用 DDD 的時候,需要在前期投入較多的時間成本來設計領域建模,所以前期成本會更高一些。
  • 但隨着業務不斷迭代後的邏輯的複雜性增加,DDD 系統架構所開發的代碼穩定性會更好,也就説明 DDD 更容易擴容和維護。
  • 所以框架結構的更換,不是最終增加開發成本的地方,如果你不做領域建模也不做更多的設計思考,那麼即使是 DDD 的四層架構,也能讓你寫出 MVC 的效果。而那些對業務場景經驗豐富的架構師或者研發人員,已經非常明確了各個業務功能的職責邊界,要實現一個系統需求需要完成哪些核心領域服務,再這樣的情況用 DDD 也不會帶來多少開發成本,反而更加遊刃有餘了!這就是為什麼説,需要領域專家,因為專家已經積累了很多的戰略設計經驗
  • 此外使用 DDD 領域驅動設計的模式進行開發,除了解決需求的迭代成本,更多的時候是要面對公司戰略調整後,系統的交接、人員的更替和新增,都要在原有的工程架構下繼續迭代開發,否則就要推翻重新做,那樣所面臨的更替成本將更大,同時又是開發了一個與人員綁定不易於交接維護的工程代碼。

三、架構對比

在瞭解和掌握 DDD 領域驅動設計的路上,你一定會碰到兩個抽象的釘子 —— “貧血模型”、“充血模型”:

  • 貧血模型:事務腳本模式,最早起源於 EJB2,到 Spring 進入開“春”盛世。
  • 充血模型:領域模型模式,2003年提出,一直到《實現領域驅動設計》的問世,才開啟了 DDD 的大門。但國內直到微服務、低代碼的興起,才開始 DDD 熱

1. MVC

MVC 分層結構將:“狀態”(數據,成員對象)、“行為“(邏輯、過程),分離到不同的對象中,只有狀態的對象(VO -> Value Object) 被稱為貧血模型,只有行為的對象,就是框架分層中常見的Logic/Service/Manager層(對應到EJB2中的Stateless Session Bean)

MVC 分層結構

  • 以應用層 Service 使用 DAO、PO 基礎設施包裝業務邏輯的開發方式,乍一看以為應用層是在對領域建模的實現,”領域層“有着豐富的對象鏈接,和真正的領域模型也非常類似,但當我們代碼隨着業務功能邏輯的逐步實現中會慢慢發現,我們寫了一堆的 get/set 對象,而他們被反覆交叉使用,沒有與任何領域聚合,也就是不具有任何的行為動作,只是一堆貧血模型對象。
  • 這種反模式的設計,其實完全與面向對象的設計是背道而馳的,面向對象的設計更希望行為和數據綁定在一起,與之對比的貧血模型更像是面向過程設計。
  • 在 MVC 分層結構下,所有的行為都被寫入到 Service 對象中,最終你會得到一組事務處理的過程腳本,從而完美的避開了領域模型設計所帶來的好處(清晰的職責邊界、聚合的功能服務、清晰的面向對象)。

2. DDD

DDD 的分層結構也是面向對象編程的本質:”一個對象擁有行為和數據“,在領域層包括了:對象、聚合對象、倉儲和Service實現。

DDD 分層結構

  • DDD 的分層結構更注重 Domain 領域層的實現,由很薄的應用層定義接口和編排接口,由領域層做具體的實現。
  • 所有的業務邏輯都按照各自的職責邊界拆分成一塊塊的功能領域,每一個功能領域都是充血模型的結構的具體實現。
  • 那麼這樣的代碼最終實現以後,無論在迭代、維護、人員更替,都能很好按照領域設計文檔找到對應的代碼實現進行開發。

四、設計原則

首先 DDD 的設計分為戰略和戰術; - 戰略設計:從業務視角出發,建立業務領域模型、劃分職責邊界,建立通用語言的界限上下文。頂層戰略設計構建的領域模型結構,是整個服務後期編排的重點,它確定了功能的職責邊界、聚合、對象等,也就絕對了後期服務戰術實現的開發和交付質量。重視戰略,才能落地好戰術! - 戰術設計:從技術視角出發,側重於領域模型的技術實現,完成功能開發和交付落地。領域設計的重點包括:實體、聚合對象、值對象、領域服務、倉儲,還有一個非常重點的設計模式。任何一個較為複雜的領域模型實現都需要考慮設計模式的使用,否則即使戰略優秀,戰術也能幹回 MVC 去。

在以DDD領域驅動設計落地的過程中,要依靠領域驅動設計的設計思想,通過事件風暴建立領域模型,合理劃分領域邏輯和物理邊界,建立領域對象及服務矩陣和服務架構圖,定義符合DDD分層架構思想的代碼結構模型,保證業務模型與代碼模型的一致性。通過上述設計思想、方法和過程,指導團隊按照DDD設計思想完成微服務設計和開發。

  1. 拒絕泥球小單體、拒絕污染功能與服務、拒絕加功能排期一個月
  2. 架構出高可用極易符合互聯網高速迭代的應用服務
  3. 物料化、組裝化、可編排的服務,提高人效
  4. 要領域驅動設計,而不是數據驅動設計,也不是界面驅動設計
  5. 要職能清晰的分層,而不是什麼都放的大籮筐

DDD 的領域模型設計,界限內的上下文,可以拆分為獨立的微服務。但不僅要從業務視角看問題,也要考慮非業務的技術因素,包括:高性能、安全、團隊、技術異構等,這些非業務的技術因素,也會決定領域模型落地的具體落地。

五、舉個例子

你説我 MVC 不好,你説我 MVC 貧血模型,PO 類不斷的膨脹,但讓我用 DDD 又都是理論,程序員更喜歡看的是已經落地的代碼,告訴我怎麼幹。

為什麼這麼難落地呢?因為從 MVC 過度到 DDD 描述對比只是積累了 MVC 失敗的教訓,但沒有 DDD 成功的經驗,所以更多的時候想落地 DDD 除了有理論支撐,更需要一份案例擺在面前。

1. 工程結構

所以為了讓更多的碼農看到在 DDD 上一條能走的路,專門折騰了個 DDD 分佈式抽獎系統,來告訴大家怎麼使用 DDD 開發業務需求;

DDD 分佈式抽獎系統,工程分佈

整體系統架構設計包含了6個工程:

  1. Lottery:分佈式部署的抽獎服務系統,提供抽獎業務領域功能,以分佈式部署的方式提供 RPC 服務。
  2. Lottery-API:網關API服務,提供;H5 頁面抽獎、公眾號開發回覆消息抽獎。
  3. Lottery-Front:C端用户系統,vue H5 lucky-canvas 大轉盤抽獎界面,講解 vue 工程創建、引入模塊、開發接口、跨域訪問和功能實現
  4. Lottery-ERP:B端運營系統,滿足運營人員對於活動的查詢、配置、修改、審核等操作。
  5. DB-Router:分庫分表路由組件,開發一個基於 HashMap 核心設計原理,使用哈希散列+擾動函數的方式,把數據散列到多個庫表中的組件,並驗證使用。
  6. Lottery-Test:測試驗證系統,用於測試驗證RPC服務、系統功能調用的測試系統。

2. 流程拆解

當我們拿到產品的 RPD 以後,並不是直接上手開發,而是需要從流程中拆解出一份面向對象設計的領域服務,舉例;

DDD 分佈式抽獎系統,流程拆解

  • 拆解功能流程,提煉領域服務,一步步教會你把一個業務功能流程如何拆解為各個職責邊界下的領域模塊,在通過把開發好的領域服務在應用層進行串聯,提供整個服務鏈路。
  • 通過這樣的設計和落地思想,以及在把流程化的功能按照面向對象的思路使用設計模式進行設計,讓每一步代碼都變得清晰易懂,這樣實現出來的代碼也就更加易於維護和擴展了。
  • 所以,你在這個過程中學會的不只是代碼開發,還有更多的落地思想實踐在這裏面體現出來。也能為你以後開發這樣的一個項目或者在面試過程中,一些實際複雜場景問題的設計思路,打下不錯的基礎。

3. 一起實踐

如果你對 DDD 實踐學習的事情感興趣,也可以一起加入DDD 分佈式抽獎系統的實踐,來吸收一份能落地的經驗。PS給自己花點錢,做有價值的投資,就當少買個皮膚了

學習鏈接:https://bugstack.cn/md/project/lottery/introduce/Lottery%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F.html

  • 具備 Java 編程基礎的研發人員,想提升自己的技術能力
  • 希望提升編碼思維,剔除到代碼中的壞味道
  • 有意願成為架構師,但還處在一定瓶頸期
  • 想加入大廠做碼農,但總感覺找不到門路

六、總結

  • DDD 並不是 Silver Bullet,你並不能指望換個了個框架結構,就能改變堆屎山⛰似的開發代碼,所帶來壞味道問題。MVC 結構一樣可以開發出好的代碼,只是它的穩定性更差,不利於長期維護和迭代。
  • DDD 的複雜性是因為缺少領域建模的經驗,如果同一個需求你已經在 MVC 的中嚯嚯的吸收了足夠的邊界上下文總結,現在換 DDD 可以讓你更快的開發代碼。
  • DDD 也並不是所有工程模型結構都複雜,DDD 是指導思想,你可以在 DDD 四層架構中因為引入 RPC 拆解各個模塊的分層,也可以因業務規模在中等及複雜度時不引入 RPC 框架,這樣的 DDD 會更加短小精幹,與 MVC 相比只是在領域層定義接口,把代碼放到 domain 層做實現,數據放到倉儲層處理。參考代碼:https://github.com/fuzhengwei/CodeGuide