最近參與了一些電商業務中臺等複雜業務系統的設計和開發,結合DDD和中臺等,
有一些架構方面的思考和體會,在這裡記錄一下。
做技術方案,核心是下面幾個問題:
-
做什麼?- 產品需求
-
業務上怎麼做?- 業務文件
-
技術上怎麼做?- 技術方案
-
程式碼怎麼實現?- 落地實現
明確了這幾個問題,可以處理大部分日常需求開發,如果是比較複雜的業務系統,就需要拆解的更精細。
比如電商的商品管理、訂單交易、促銷活動營銷中心等系統的開發和重構,業務相對複雜,開發人天在幾個月以上,直接開發可能會老虎啃天,無從下手。
這時候可以通過一個流程化的模板來指導,如果抽象一個通用的流程,可以參考下面的套路:
-
業務拆解 > 複雜度來源 > 核心挑戰點
-
領域驅動設計 > 業務過程分析 > 領域模型抽象 > 模型分解
-
分層組織 > 工程架構 > 模組化 > 元件化
-
考慮功能複用 > 可選路徑 —( 業務身份,能力,擴充套件點,工作流程,編排)
-
方案產出 > 整體-模組-流程-細節 > 方案評審 > 最終方案
其中的功能複用環節,是包括阿里在內的大部分業務中臺的解決思路,僅供參考。
一、業務拆解
1.1 複雜度來源
為什麼要關注複雜度?
我比較認同系統設計中「軟體複雜度」的觀點,架構設計的目的是為了解決軟體系統的複雜度帶來的問題,所以在設計架構時,首先就要分析系統的複雜度。
只有正確分析出了系統的複雜性,後續的架構設計方案才不會偏離方向;
否則,如果對系統的複雜性判斷錯誤,即使後續的架構設計方案再完美再先進,都是南轅北轍,做的越好,錯的越多、越離譜。
舉個例子,醫院管理應用的醫療管理系統(HIS),複雜度在於業務邏輯複雜,系統之間呼叫不清晰,
如果你設計一個QPS幾萬的高效能架構,就是沒有解決系統的核心問題。
正確的做法是將主要的複雜度問題列出來,然後根據業務、技術、團隊等綜合情況進行排序,優先解決當前面臨的最主要的複雜度問題。
1.2 核心挑戰點
射人先射馬,擒賊先擒王。
確定了複雜度,也就確定了系統設計的難點,在進行系統設計時,可以把難點列出來,各個擊破。
以優惠券為例,促銷活動最大的複雜度來自營銷形態的變化,營銷最大的不變就是變,亂花漸欲迷人眼,各類促銷方式千變萬化。
每次促銷活動都有不同的玩法和定義,促銷系統的設計必須對促銷模式有所抽象,任何活動或優惠手段都是基於最基本的促銷模式而建立的。
電商促銷活動和優惠券建設難點包括:
-
底層模型抽象:底層模型抽象可以通過DDD的方式,對領域模型和進行抽象。
-
促銷引擎效能:效能問題如何解決?已經是老生常談,工程領域有很多經典的解決方案,比如快取,非同步,最終一致性。
-
關聯絡統互動:理清和關聯絡統的互動
二、領域驅動設計
軟體系統的目的反映在業務上,都是來解決一系列問題,例如考試系統完成考試,電商就是賣貨,
同一個領域的系統都具有相同的核心業務,因為他們要解決的問題的本質是類似的,一個領域本質上可以理解為一個問題域 。
只要確定了系統所屬的領域,那麼這個系統的核心業務,即要解決的關鍵問題就基本確定了。
任何一個系統都會屬於某個特定的領域,例如論壇系統,核心功能是確定的,比如使用者發帖,回帖等基本功能。
廣義的電商系統也是一個領域,做電商業務,必須要支援的商品,訂單,交易,物流等功能。
多說一句,領域驅動設計本身有一堆操作,我覺得不要把簡單的問題複雜化了,領域驅動設計不是銀彈,也沒必要妖魔化。
作為指導思想,在實際操作中,要具體問題具體分析,綜合採用其它各種設計方法。
2.1 領域模型設計
DDD裡有領域專家的概念,領域專家要在這個領域深入研究,只有這樣才會遇到非常多的該領域的問題,積累比較更加豐富的經驗。
通常來說,一個領域有且只有一個核心問題,也就是核心子域。在核心子域、通用子域、支撐子域梳理的同時,會定義出子域中的限界上下文及其關係,用它來闡述子域之間的關係 。
以電商營銷為例,優惠券、抽獎、套餐等,都是圍繞這個促銷這個業務範圍來進行的,在促銷域之外,還有相關的使用者、商品、訂單、風控、商家等。
三、架構分層
3.1 架構分層
下圖是領域驅動設計中經典的分層架構:
使用者介面/展示層:
-
請求應用層獲取使用者所需的展示資料;
-
傳送命令給應用層執行使用者的命令
應用層:
薄薄的一層,定義軟體要完成的任務。
對外為展示層提供各種應用功能,對內呼叫領域層(領域物件或領域服務)完成各種業務邏輯。應用層不包含業務邏輯
領域層:
表達業務概念、業務狀態資訊及業務規則,是業務軟體的核心
基礎設施層:
為其他層提供通用的技術能力,提供了層間通訊;為領域層提供持久化機制。
這是一個相對簡單的分層架構,其實已經老生常談,那麼問題來了,我們在上面拆解的領域模型,如何對映到更加複雜的工程架構中?
3.2 工程架構
DDD的核心訴求就是將業務架構對映到系統架構上,在響應業務變化調整業務架構時,也隨之變化系統架構。
微服務追求業務層面的複用,設計出來的系統架構和業務一致,不過領域模型並不直接反映資料結構,需要明確這一點。
領域驅動設計最後落地到資料儲存上,不需要直接參考領域模型,在最後的技術架構上可以自由選擇合適的技術架構。
3.3 模組組織
Java專案一般是典型的Maven多模組專案,可以使用不同的Module,區分各個層次,進一步,通過Package來控制DDD中的限界上下文。
3.4 領域物件
對具體的領域物件封裝時,典型的有充血和貧血模型,由於大部分程式設計師習慣在Service裡封裝業務邏輯的貧血模型,完全的充血模型開發效率相對較低,
我自己的體會是,技術服務業務第一,在開發時可以靈活的選擇實現策略,模型物件封裝一些簡單的靜態方法,大部分業務邏輯還是放在領域服務中實現。
3.5 程式碼模型
DDD 是一個指導思想,沒有一個標準的程式碼模型。而且我覺得,團隊成員水平不同,編碼習慣也有區別,如果打著DDD的旗號,來給編碼過程新增很多約束,那就有點捨本逐末了。
一般來說,可以通過一些腳手架工具,定製一個相對通用的程式碼模板,比如阿里巴巴的 https://start.aliyun.com/bootstrap.html 腳手架生成。
下面是一個簡單的程式碼模型,可以作為參考:
四、考慮功能複用
4.1 程式設計DRY原則
大家都知道,編寫整潔程式碼,有一個非常重要的原則就是DRY,
Don't Repeat Yourself,避免產生重複程式碼,有經驗的程式設計師都能夠意識到這一條約束。
如果你使用Idea開發,Idea也會識別並且提示你重複的程式碼,建議你進行抽象。
DRY的好處更少的程式碼是好的,它節省了時間和精力,易於維護,並且減少了bug的機率。
除了在軟體開發領域,在業務系統層面,也存在如何避免重複能力建設,考慮業務複用的問題。
4.2 業務層面的DRY
業務系統層面的DRY原則,其實可以總結為一個問題,就是複雜業務系統,如何實現具有共性的業務能力的複用,這個也是很多業務中臺關注的問題。
一般的,大部分業務中臺(特指業務中臺,不包括資料中臺等其他形態)對業務複用的方式,
都可以通過定義業務身份 ——> 梳理擴充套件點 ——> 列舉業務能力 ——> 根據不同業務身份編排工作流 ——> 實現業務能力複用,這樣的流程來實現。
可以對比程式設計中的Pipeline模式,或者責任鏈模式,只不過每個鏈條上負責處理輸入和輸出的,是不同的業務功能。
業務中臺是另一個話題,這部分是發散思考,具體可以參考阿里巴巴 TMF (Trade Mudule Framework)框架的介紹:
如何實現32.5萬筆/秒的交易峰值?阿里交易系統TMF2.0技術揭祕
五、可擴充套件性和過度設計如何平衡
好的架構設計一定是擴充套件簡單的。
在設計時,要儘量封裝可能的變化,在業務流程發生一些調整時,能夠比較方便地修改系統程式模組或元件間的呼叫關係而實現新的需求,也就是我們常說的可擴充套件性。
但是可擴充套件性本身也是系統設計的複雜度來源之一,這就涉及到一個問題,如何平衡可擴充套件和過度設計。
5.1 區分確定性和變化
好的架構一定是擴充套件簡單,執行平穩的。
系統架構最開始可以從一個通用的流程開始,case-by-case,
然後將「變化少」的部分沉澱下來為架構,將「變化多」的沉澱為擴充套件或者配置,梳理清楚,將這兩者結合起來,最後完成系統架構設計。
5.2 用容量規劃的方式來處理擴充套件程度
可以使用容量規劃的思想,來處理可擴充套件性設計。
在做技術方案時,容量規劃是一個特別重要的環節,要預估未來幾年的增長量,進行資料庫和快取的容量規劃。
我覺得這個方式也可以應用在擴充套件性設計上,對業務變化進行預期,考慮技術方案能夠支援的業務發展時間。
六、方案評審
好的技術方案很難一蹴而就,大部分時候要經過反覆的調整,就是需要關聯的各方參與方案的評審和修改,最終確定最終技術方案。
我的建議是,團隊最好輸出一份技術方案的規範,可以供每個成員參考,從設計階段,就統一團隊成員的認識。
七、總結
最後再總結一下,關於複雜業務系統開發的一些體會:
-
熟悉業務,抽象產品需求,分析相關測試用例,瞭解各種使用者角色和其使用的場景
-
自頂向下進行方案設計,對於比較複雜的業務系統,比較好的方式是先關注頂層模型,避免在一開始就陷入技術和業務細節中去
-
從整體設計,到模組區域性規劃,設計好部署架構、分層和分模組、API設計、資料庫設計等
-
可以參考成熟的解決方案,比如將開源軟體,改造,變成適合自己業務需求的架構
-
驗證和優化架構設計方案,完整的架構設計方案,需要有多次的評審,充分收集各方面的反饋,反覆修改後確定
-
合理進行擴充套件,考慮架構預期能滿足多長時間的業務增長,比如未來一年的業務變化