一個電商供應(yīng)鏈系統(tǒng)的DDD實戰(zhàn)

作者 | 武清明
任何一套業(yè)務(wù)架構(gòu)都可能存在一定的歷史問題,這是業(yè)務(wù)在不同階段做技術(shù)選型必然出現(xiàn)的狀況,如何用新的、合適的架構(gòu)思想做恰到好處地改造,則是架構(gòu)師們的必備能力。本文是 Keep 利用 DDD 改造電商供應(yīng)鏈系統(tǒng)的一次精彩實戰(zhàn),InfoQ 架構(gòu)頭條獨家分享,以供大家參考交流。
文章作者:武清明,目前他在 Keep 負責(zé)商業(yè)化業(yè)務(wù)中臺研發(fā)和規(guī)劃工作,擅長電商業(yè)務(wù)系統(tǒng)架構(gòu)設(shè)計,采用 DDD 合理簡單化設(shè)計復(fù)雜電商系統(tǒng),提升系統(tǒng)功能模塊的復(fù)用性和擴展性。
今天的主角是供應(yīng)鏈系統(tǒng),又被稱為進銷存系統(tǒng)。這個系統(tǒng)主要是針對采購(進)—>入庫(存)—>銷售(銷)動態(tài)鏈條的管理系統(tǒng),核心能力是管理倉庫貨物庫存,在電商體系中起到承上啟下的作用,下圖中的 Skynet 系統(tǒng)和 ERP 系統(tǒng)分別扮演著供應(yīng)鏈系統(tǒng)的核心角色,負責(zé)訂單發(fā)貨、售后退貨、采購補貨、倉間調(diào)撥以及特殊出入庫等核心流程。

Skynet 系統(tǒng)和 ERP 系統(tǒng)作為元老級系統(tǒng),自 Keep 開啟電商賽道時開始建設(shè),經(jīng)過多年需求快速迭代,期間系統(tǒng)包袱越來越重,運營過程中的問題也越來越多。供應(yīng)鏈系統(tǒng)相對于 Keep 電商業(yè)務(wù)發(fā)展明顯滯后,甚至有可能進一步阻礙 Keep 電商業(yè)務(wù)發(fā)展,而當(dāng)時的供應(yīng)鏈系統(tǒng)因缺乏系統(tǒng)性規(guī)劃、代碼缺少規(guī)范,導(dǎo)致這個元老級系統(tǒng)積重難返。當(dāng)時面臨的主要問題如下:
系統(tǒng)邊界不清晰
架構(gòu)混亂,系統(tǒng)內(nèi)部分層不清晰
越來越模糊 usecase,導(dǎo)致代碼邊界和事務(wù)不清晰; 分層后各層職責(zé)和接口職責(zé)不清晰,導(dǎo)致接口依賴混亂,甚至出現(xiàn)循環(huán)依賴。

庫存不準,庫存變更上下文不清晰
庫存不準,超賣甚至少賣情況頻繁 庫存變更日志不規(guī)范,上下文不清晰,出現(xiàn)庫存問題時,查找原因困難重重 庫存與庫存變更日志無法自證正確 如果您正在學(xué)習(xí)Spring Boot,推薦一個連載多年還在繼續(xù)更新的免費教程:http://blog.didispace.com/spring-boot-learning-2x/ 業(yè)務(wù)新要求
店鋪庫存分配自動化 智能采購 庫存準確率保障 履約率保障 提升運營效率
種種問題重壓,在老系統(tǒng)上修改已無法根除系統(tǒng)問題,且無法滿足未來業(yè)務(wù)發(fā)展需求,導(dǎo)致供應(yīng)鏈系統(tǒng)正式提上日程。
重構(gòu)思路主要包括三大類梳理,分別是:
梳理庫存業(yè)務(wù)場景

梳理限界上下文

梳理庫存模型
占用庫存:已售賣未出庫庫存數(shù) 可用庫存:倉庫實物庫存 - 占用庫存 實物庫存:倉庫中的實際庫存數(shù) 在途庫存:已采購未入庫庫存數(shù) 凍結(jié)庫存:因秒殺等促銷活動或倉間調(diào)撥等預(yù)占的庫存數(shù)
梳理清楚之后,關(guān)于 DDD 架構(gòu)選型也是要重點考慮的內(nèi)容:
梳理領(lǐng)域模型與非領(lǐng)域模型之間關(guān)系 - 六邊形架構(gòu)

保證核心領(lǐng)域模型的穩(wěn)定性
分層設(shè)計采用依賴倒置原則,保證核心領(lǐng)域模型的穩(wěn)定性,領(lǐng)域?qū)硬灰蕾嚾魏纹渌麑樱讓臃?wù)可以依賴高層服務(wù)所提供的接口。

防止定制化查詢腐化領(lǐng)域模型

防止與其他限界上下文交互導(dǎo)致領(lǐng)域模型腐化
如下圖所示采購上下文通過防腐層 (ACL) 將倉儲庫存核心上下文中的倉庫信息映射為自身上下文中的倉庫值對象,防止倉庫信息依賴腐化。

架構(gòu)最終落地 -COLA

庫存變更場景相關(guān)單據(jù)狀態(tài)一致性保障
從庫存變更場景中,可以看到圍繞庫存變更在不同的業(yè)務(wù)層存在不同的業(yè)務(wù)單據(jù),上層業(yè)務(wù)層單據(jù)狀態(tài)變更依賴底層倉儲核心單據(jù)狀態(tài)變更,如采購入庫單入庫狀態(tài)變更為入庫完成則采購單狀態(tài)也會變更為已完成,如銷售出庫單狀態(tài)變更為出庫完成則銷售發(fā)貨單狀態(tài)會變更為已發(fā)貨。
另外,如果您正在學(xué)習(xí)Spring Cloud,推薦一個連載多年還在繼續(xù)更新的免費教程:https://blog.didispace.com/spring-cloud-learning/
方案選擇

最終我們采用 EventStore 方案,使用 EventStore 數(shù)據(jù)流程如下:

上圖中黃色部分為領(lǐng)域事件異常處理。
發(fā)布領(lǐng)域事件代碼如下:



訂閱領(lǐng)域事件
注冊訂閱組

在訂閱組中聲明訂閱事件

在持續(xù)集成開發(fā)過程中如何同時保障效率和質(zhì)量 - 單元測試保駕護航
核心領(lǐng)域模型添加單元測試,對應(yīng) Domain 測試 核心業(yè)務(wù)接口場景添加單元測試,對應(yīng) CmdExe 測試 引入 Mockito 庫,mock 相關(guān)接口和數(shù)據(jù),驗證流程環(huán)節(jié)是否正確 在單測代碼中造單測相關(guān)數(shù)據(jù),保證單測數(shù)據(jù)可靠性 單測采用 H2 數(shù)據(jù)庫,避免測試過后留痕,影響后續(xù)單測,同時提升單測執(zhí)行效率 減少或不依賴其他中間件,如 Dubbo、Kafka 等,如依賴可考慮直接 Mock git push 后 CI 開啟自動單元測試
最終,回顧這次改造工作,我認為收益可以分為五點:
實際庫存準確,徹底解決倉庫庫存不準問題,同時為校準銷售庫存提供基準參考; 功能擴展方便,如后續(xù)快速對接財務(wù)系統(tǒng); 快速定位問題(代碼結(jié)構(gòu)清晰,庫存變更有據(jù)可查且上下文清晰); 沉淀出較通用的事件組件 EventStore,后續(xù)在 Keep 電商內(nèi)部快速推廣復(fù)用; 沉淀出一套比較成熟的 DDD 最佳實踐,后續(xù)快速推廣至 Keep 電商庫存系統(tǒng)重構(gòu)、售后重構(gòu)。
可以看出,收益還是非常喜人的。大部分同學(xué)關(guān)注 DDD 是因為微服務(wù),沒錯,DDD 可以說是與微服務(wù)天生互補的,DDD 領(lǐng)域面向劃分業(yè)務(wù)模型邊界,微服務(wù)面向?qū)误w架構(gòu)拆分為多個微服務(wù),至于如何拆微服務(wù),DDD 領(lǐng)域拆分則是一個非常好的微服務(wù)拆分方式。
歡迎關(guān)于 DDD,如果你想進一步交流探討,也可以在本文下留言,期待大家的分享能夠帶來更多的啟發(fā)。
作者介紹
武清明,從業(yè) 12 年,近 8 年一直在互聯(lián)網(wǎng)電商行業(yè)一線從事系統(tǒng)研發(fā),之前在京東和萬達電商負責(zé)過倉儲系統(tǒng)、訂單系統(tǒng)、促銷系統(tǒng)等研發(fā)工作。目前在 Keep 負責(zé)商業(yè)化業(yè)務(wù)中臺研發(fā)和規(guī)劃工作。擅長電商業(yè)務(wù)系統(tǒng)架構(gòu)設(shè)計,采用 DDD 合理簡單化設(shè)計復(fù)雜電商系統(tǒng),提升系統(tǒng)功能模塊的復(fù)用性和擴展性。
往期推薦
技術(shù)交流群
最近有很多人問,有沒有讀者交流群,想知道怎么加入。加入方式很簡單,有興趣的同學(xué),只需要點擊下方卡片,回復(fù)“加群“,即可免費加入我們的高質(zhì)量技術(shù)交流群!
點擊閱讀原文,送你免費Spring Boot教程!
