作者:小傅哥
部落格: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)
- 以應用層 Service 使用 DAO、PO 基礎設施包裝業務邏輯的開發方式,乍一看以為應用層是在對領域建模的實現,”領域層“有著豐富的物件連結,和真正的領域模型也非常類似,但當我們程式碼隨著業務功能邏輯的逐步實現中會慢慢發現,我們寫了一堆的
get/set
物件,而他們被反覆交叉使用,沒有與任何領域聚合,也就是不具有任何的行為動作,只是一堆貧血模型物件。 - 這種反模式的設計,其實完全與
物件導向
的設計是背道而馳的,物件導向的設計更希望行為和資料繫結在一起,與之對比的貧血模型更像是程式導向設計。 - 在 MVC 分層結構下,所有的行為都被寫入到 Service 物件中,最終你會得到一組事務處理的過程指令碼,從而完美的避開了領域模型設計所帶來的好處(清晰的職責邊界、聚合的功能服務、清晰的物件導向)。
2. DDD
DDD 的分層結構也是物件導向程式設計的本質:”一個物件擁有行為和資料“,在領域層包括了:物件、聚合物件、倉儲和Service實現。
- DDD 的分層結構更注重 Domain 領域層的實現,由很薄的應用層定義介面和編排介面,由領域層做具體的實現。
- 所有的業務邏輯都按照各自的職責邊界拆分成一塊塊的功能領域,每一個功能領域都是充血模型的結構的具體實現。
- 那麼這樣的程式碼最終實現以後,無論在迭代、維護、人員更替,都能很好按照領域設計文件找到對應的程式碼實現進行開發。
四、設計原則
首先 DDD 的設計分為戰略和戰術;
- 戰略設計:從業務視角出發,建立業務領域模型、劃分職責邊界,建立通用語言的界限上下文。頂層戰略設計構建的領域模型結構,是整個服務後期編排的重點,它確定了功能的職責邊界、聚合、物件等,也就絕對了後期服務戰術實現的開發和交付質量。重視戰略,才能落地好戰術!
- 戰術設計:從技術視角出發,側重於領域模型的技術實現,完成功能開發和交付落地。領域設計的重點包括:實體、聚合物件、值物件、領域服務、倉儲,還有一個非常重點的
設計模式
。任何一個較為複雜的領域模型實現都需要考慮設計模式的使用,否則即使戰略優秀,戰術也能幹回 MVC 去。
在以DDD領域驅動設計落地的過程中,要依靠領域驅動設計的設計思想,通過事件風暴建立領域模型,合理劃分領域邏輯和物理邊界,建立領域物件及服務矩陣和服務架構圖,定義符合DDD分層架構思想的程式碼結構模型,保證業務模型與程式碼模型的一致性。通過上述設計思想、方法和過程,指導團隊按照DDD設計思想完成微服務設計和開發。
- 拒絕泥球小單體、拒絕汙染功能與服務、拒絕加功能排期一個月
- 架構出高可用極易符合網際網路高速迭代的應用服務
- 物料化、組裝化、可編排的服務,提高人效
- 要領域驅動設計,而不是資料驅動設計,也不是介面驅動設計
- 要職能清晰的分層,而不是什麼都放的大籮筐
DDD 的領域模型設計,界限內的上下文,可以拆分為獨立的微服務。但不僅要從業務視角看問題,也要考慮非業務的技術因素,包括:高效能、安全、團隊、技術異構等,這些非業務的技術因素,也會決定領域模型落地的具體落地。
五、舉個例子
你說我 MVC 不好,你說我 MVC 貧血模型,PO 類不斷的膨脹,但讓我用 DDD 又都是理論,程式設計師更喜歡看的是已經落地的程式碼,告訴我怎麼幹。
為什麼這麼難落地呢?因為從 MVC 過度到 DDD 描述對比只是積累了 MVC 失敗的教訓,但沒有 DDD 成功的經驗
,所以更多的時候想落地 DDD 除了有理論支撐,更需要一份案例擺在面前。
1. 工程結構
所以為了讓更多的碼農看到在 DDD 上一條能走的路,專門折騰了個 DDD 分散式抽獎系統
,來告訴大家怎麼使用 DDD 開發業務需求;
整體系統架構設計包含了6個工程:
- Lottery:分散式部署的抽獎服務系統,提供抽獎業務領域功能,以分散式部署的方式提供 RPC 服務。
- Lottery-API:閘道器API服務,提供;H5 頁面抽獎、公眾號開發回覆訊息抽獎。
- Lottery-Front:C端使用者系統,vue H5 lucky-canvas 大轉盤抽獎介面,講解 vue 工程建立、引入模組、開發介面、跨域訪問和功能實現
- Lottery-ERP:B端運營系統,滿足運營人員對於活動的查詢、配置、修改、稽核等操作。
- DB-Router:分庫分表路由元件,開發一個基於 HashMap 核心設計原理,使用雜湊雜湊+擾動函式的方式,把資料雜湊到多個庫表中的元件,並驗證使用。
- Lottery-Test:測試驗證系統,用於測試驗證RPC服務、系統功能呼叫的測試系統。
2. 流程拆解
當我們拿到產品的 RPD 以後,並不是直接上手開發,而是需要從流程中拆解出一份物件導向設計的領域服務,舉例;
- 拆解功能流程,提煉領域服務,一步步教會你把一個業務功能流程如何拆解為各個職責邊界下的領域模組,在通過把開發好的領域服務在應用層進行串聯,提供整個服務鏈路。
- 通過這樣的設計和落地思想,以及在把流程化的功能按照物件導向的思路使用設計模式進行設計,讓每一步程式碼都變得清晰易懂,這樣實現出來的程式碼也就更加易於維護和擴充套件了。
- 所以,你在這個過程中學會的不只是程式碼開發,還有更多的落地思想實踐在這裡面體現出來。也能為你以後開發這樣的一個專案或者在面試過程中,一些實際複雜場景問題的設計思路,打下不錯的基礎。
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