一. 背景
本文以預算管控服務建設作為一個DDD設計的例子介紹,目標是是呈現一次DDD設計的程序,為了減少繪圖和描述的作業量,文中會對預算管控業務需求和功能做簡化,請重點關注設計的流程,這是我們想傳達的重點,忽略設計細節的合理性,
另外,對于預算管控服務來講,不一定要用DDD來進行分析設計,基于傳統的資料驅動就完全可以滿足需求,但作為介紹DDD實施程序,預算管控是一個不錯的例子(不需要畫太多的圖),在這里我們不討論什么型別專案合適DDD,可以參考:
大致的共識為復雜度高的業務適合DDD,而復雜度一般體現在:
業務流程長
業務場景多
業務概念多
業務系統干系人多
業務系統需要長期維護且持續有變更
業務背景
需要設計一個適用于本地生活場景的資源預算規劃和管控服務,業務需求上主要包括兩方面的用例:
-
品牌發放權益需要有一定的限額,不能無限制的發放,包括品牌、門店、活動、人群、權益等維度
-
個人消費者參與活動領取權益有次數的限制,不能無限額領取或使用,包括在活動、品牌、門店、商品等維度
目前各業務線針對以上需求,各自實作了部分能力,整體上看較零碎、不完善、不統一,本次目標是設計一個統一的平臺為各業務方提供基礎能力
二. 戰略設計
2.1 業務梳理
2.1.1 業務定位&目標分析
協同分析階段,需要各干系方共同參與,如,業務運營,業務產品,運營產品,平臺架構,業務系統方的技術等,
目標:聚焦業務需求和平臺定位,確定平臺的能力范圍和服務方式
輸出需求檔案:
-
提供一個統一的記賬能力,以平臺的方式為各個系統提供記賬服務,
主要功能:
-
記賬
-
銷賬
-
各維度的查賬
-
庫存創建
-
庫存扣減、查詢
-
庫存縮擴容
細化要求:
-
作為平臺能為客戶提供邏輯上的資料隔離,即A產品方默認不能訪問B產品方資料,如需要訪問需要經過授權同意
-
作為平臺需要提供同步記賬能力和異步記賬能力,并提供明確的“能力范圍”承諾
-
作為平臺需要為產品方提供方案避免重復記賬
-
除記賬之外,需要提供對應的銷賬能力
-
需要提供常用的記賬周期(賬期),如時賬,日賬,周賬,月賬,季度賬,年度賬,終身賬,
-
需要提供自定義記賬周期(賬期)的能力
-
需要提供一單多賬記賬能力,即一張單據,需要同時記錄日賬和終身賬
-
需要提供多維度的查賬能力,如按產品方,記賬主體,產品,賬期時間,以及基于這些維度的組合條件查賬
-
需要提供批量查賬能力,如主體下單一產品的批量賬期時間,主體下的批量產品的單一賬期時間,及其它可能的批量組合
-
技術上需要保證賬單存盤和記賬動作的事務
-
技術上需要保證分庫分表的資料存盤均衡性
-
技術上需要盡量保證分庫分表的資料庫讀寫均衡分布,對可能出現的資料傾斜場景,需要給出明確的說明,和使用限制性規范
-
能提供性能基準承諾,由測驗團隊對典型場景壓測給出《平臺性能報告》,作為平臺對外服務的一部分
-
庫存的創建,扣減,查詢,擴容,縮容(縮容量不能少于剩余庫存)
-
庫存凍結,解凍
-
庫存管理
主要功能:
-
庫存創建
-
庫存擴容
-
庫存縮容
-
庫存扣減
-
庫存回補
-
庫存查詢
細化要求:
-
滿足去UMP的所有要求(去UMP為一個內部專案,各種限定型規則在此不細列)
2.1.2 業務抽象可視化
通過事件風暴或四色建模法來可視化,我們這里選擇事件風暴法,程序主要涉及
-
識別領域名詞(示意,不包括全部)

-
識別領域命令(示意,不包括全部)
這里列了主要的命令

-
場景分析:
主要是識別發出命令的主體是誰,如C端消費者,B端消費者還是某個系統,主要是通個主體在具體Usecase中去串聯命令對于領域物件(對應領域名詞)的影響,串聯業務流程完成領域分析
-
識別領域事件
在命令發出后對一個領域物件(聚合根)將產生影響,往往對內(聚合根)會生成資料或發生狀態變更;對外(向其他聚合根)發送訊息或觸發事件,
這些事件是業務專家重點關心的結果
這里是先識別領域事件,還是先識別命令可以根據設計者的習慣和熟悉度,自行選擇
最后,整合命令,領域物件和領域事件的關系,得到業務梳理的輸出檔案(實際命令可能比圖中多,如庫存凍結和預扣等):

2.2 統一領域語言(示意,不包括全部)
2.1章中幾個階段是一個來回討論的階段,通常需要經過很多輪的修改和妥協,以至于早期列出的領域名詞、領域事件和命令遠多于上面的圖例,但最后大家需要統一確定其中關鍵的領域名詞、領域事件,并統一領域語言,在后續的討論和設計階段均使用統一語言建模,這里我們用下面的統一語言僅示例產品賬:
| 術語 | 描述 |
| 記賬主體(principal)(mainPrincipal)(subPrincipal) | 記賬主體(id),如,抽獎活動中的消費者記賬,則為cid |
| 賬單(accounting document)(accounting doc) | 名詞,一次記賬請求提交的資料為一條記錄,指產品方提交給記賬平臺的原始單據資料 |
| 記賬(keep account) | 動詞,記錄record的程序 |
| 銷賬(write off account) | 動詞組,記賬的反向操作 |
| 金額(amount) | 記賬的數量 |
| 賬(account) | 按賬期 統計的在該周期內的數額總和相關資料 |
| 賬期(account cycle) | 賬期(會計周期)的型別,如,日賬,月賬,終身賬等 |
| 賬期值(account cycle value) | 賬期值,如對于自然日型別的賬期,賬期值可以是“20210415”代表4月15這天的賬 |
| 記賬型別(operate type) | 操作型別指,記賬或銷賬 |
2.3 限界上文識別
最后,當領域名詞、領域事件和命令都統一并清理好之后,我們需要圈定合適領域出來,這里要注意,并沒有統一的最佳答案,圈定原則只是遵循現實世界的松緊耦合關系,某些場景下可能有多種選擇,本例較簡單,示例如下

2.4 問題子域識別
在戰略設計階段的最后,按“一個子域負責解決一個獨立業務價值的問題”的原則,將限界背景關系劃分到不同的問題子域(Subdomain)中,同時還需要從更大的域視角來俯覽全域,并按照以下三種型別進行標注:
-
核心域(Core Domain):是當前產品的核心差異化競爭力,是整個業務的盈利來源和基石,如果核心域不存在,那么整個業務就不能運作,對于核心域,需要投入最優勢的資源(包括能力高的人),和做嚴謹良好的設計,
-
通用子域(Generic Subdomain):該類問題在界內非常常見,所以很可能有現成的解決方案,通過購買或簡單修改的方式就可以使用,
-
支撐子域(Supporting Subdomain):該類問題解決的是支撐核心域運作的問題,其重要程度不如核心域,又不屬于通用子域,具備強烈的個性化需求,難以在業內找到現成的解決方案,需要專門的團隊定制開發,
問題子域,是對業務問題的進一步澄清和劃分,同時也是對于資源投入優先級的重要參考,相對限界背景關系來說,問題子域是對業務問題更大粒度的劃分,是在限界背景關系識別后與問題域匹配的一個程序,
通過對于子域進行識別、劃分和型別標注,團隊能夠實作軟體架構在業務邊界上的內聚和解耦,便于逆向應用“康威定律”,
在 DDD 的概念中,限界背景關系和問題子域是兩個不同維度的概念,限界背景關系可能只是真實問題子域的一部分表達,也可能限界背景關系中的一些領域名詞超出實際問題子域的范圍,理論上來說沒有絕對的依賴關系,需要根據實際需求和成本綜合考慮,既要保證便資源分配的合理,又要在降低落地成本的同時保證后期演進的適度兼容,
問題子域識別程序的產出物,如下圖所示:

2.5 限界背景關系映射(示意,不包括全部)
這里只示例產品賬的,明確限界背景關系映射關系,是為了更明確各context之間的關系,在IDDD中給出了9種關系,在本例種只涉及到3種,實際專案中可能比這個復雜的多,尤其是涉及集成和遺留系統的場景,
明確contex之間關系,有助于后續保證系統之間的依賴關系,為后續架構模式的補充模塊做好準備,

三. 戰術設計
3.1領域建模
3.1.1 領域物件提取(聚合/物體識別)
偷個懶,這里只示意產品賬的物體和部分值物件

3.2 業務服務識別
業務服務識別,是為后續系統實作進行的基于業務邊界的模塊拆分分析,常見的拆分方法有:
-
基于限界背景關系進行拆分:每個限界背景關系為一個服務,優點是每個服務都很小,代碼量少;缺點是拆分粒度太細,導致服務數量過多,增加架構設計的復雜度和運維成本,
-
基于子域進行拆分:每個子域為一個服務,優點是服務數量相對較少,架構復雜度和運維成本相對更低;缺點是拆分粒度在某些場景下會非常大,導致單個服務變成“小單體”,增加開發成本和代碼分層復雜度,
通過對于業務服務進行劃分,團隊能夠獲得對軟體架構模塊拆分的直接指導,并且還能夠依據“逆康威定律”依據架構結果進行開發團隊的劃分和組建,
下面是預算管控子域的服務拆分示例
| 子域 | 服務 |
| 預算管控子域 | 庫存服務 |
| 產品賬服務 |
3.3 業務服務介面識別
單獨對業務服務的介面能力進行識別,是符合面向介面編程原則的,提前定義服務的概要設計方案,可以讓后續團隊成員更快開展作業,也方便后續介面的詳細設計
這里提前識別服務介面,是為了避開技術實作細節的影響,我們在基于具體技術實作的情況下設計介面,通常會干擾領域驅動的設計,我們試想下基于swagger檔案,設計API時,我們是否容易保證API的歸屬正確領域服務,所以提前的概要識別和設定很重要
下面是庫存和賬服務介面識別示例:
| 子域 | 聚合根/物體 | 介面能力 | 讀寫 |
| 賬背景關系 | 賬單 | 記賬 | 寫 |
| 賬本 | 單主體單產品單賬期查賬 | 讀 | |
| 單主體批量產品單賬期查賬 | 讀 | ||
| 庫存背景關系 | 庫存 | 創建庫存 | 寫 |
| 扣減庫存 | 寫 | ||
| 縮擴容庫存 | 寫 | ||
| 查詢庫存 | 讀 |
四. 技術實作
在完成了戰略設計和戰術設計之后,就可以考慮具體的技術詳設,這個階段會設計到具體的架構模式選擇,架構風格和基礎技術,存盤等的選擇,
包括且不限于:
-
架構風格選擇,單體,soa,微服務,restful,rpc,webservice,ODATA等
-
架構模式選擇,傳統分層,六邊形,簡潔,洋蔥等
-
補全組件,如rpc客戶端,mtop,gatway,acl等,這里要分清應用層,,基礎設施和領域
-
技術框架選型,技術堆疊,服務治理體系
-
API設計,openapi,swagger,blueprint等
-
領域模型類設計,參考領域模型設計類圖
-
持久化選擇,這里要考慮哪些需要存盤RDB,哪些用Nosql,哪些只需要記憶體中,在上例產品賬中的賬本就不需要持久化
-
應用層設計模式選擇,因應用需要,或運營策略需要支持能力要考慮合適的模式支持
-
考慮其他需求的實作,易測驗性,性能,易維護和運維,安全等
在本例里只示例產品賬的領域模型參考:
其中賬本(accountbook)不需要持久化,其他領域物件均需要持久化
五. 總結
最后需要時刻提醒的,沒到最后實作階段之前應該杜絕提前考慮技術細節和技術實作,否則很容易偏離DDD
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/287911.html
標籤:其他
