系統(tǒng)改造:一次系統(tǒng)領(lǐng)域拆分的實(shí)戰(zhàn)復(fù)盤
引言
在互聯(lián)網(wǎng)的浪潮中,每一個(gè)成功的產(chǎn)品背后都有著不為人知的技術(shù)挑戰(zhàn)。當(dāng)業(yè)務(wù)量從千級(jí)躍升到百萬級(jí),當(dāng)用戶從幾百人暴增到幾十萬人,原本運(yùn)行良好的系統(tǒng)突然變得不堪重負(fù)。這時(shí)候,架構(gòu)師面臨的不僅僅是技術(shù)問題,更是對(duì)業(yè)務(wù)理解深度和技術(shù)前瞻性的終極考驗(yàn)。
今天,我想跟大家分享一個(gè)真實(shí)的故事——一個(gè)關(guān)于系統(tǒng)拆分重構(gòu)的故事!
當(dāng)系統(tǒng)開始"呼救"
故事要從一個(gè)朋友的求助電話開始。那是一個(gè)周五的晚上,電話里傳來他焦急的聲音:"系統(tǒng)快撐不住了,能幫忙看看嗎?"
他們公司是某行業(yè)知名電商的供貨商,業(yè)務(wù)模式比較特殊。不像傳統(tǒng)電商那樣簡(jiǎn)單的買賣關(guān)系,他們的供應(yīng)鏈很長(zhǎng),涉及多級(jí)供貨商、多個(gè)工廠,還要協(xié)調(diào)各種材料商的生產(chǎn)排期。為了提高協(xié)調(diào)效率,他們?cè)谠械挠唵蜗到y(tǒng)基礎(chǔ)上增加了排期協(xié)商功能。
剛開始一切都很順利,但隨著業(yè)務(wù)量的激增,問題開始暴露。訂單數(shù)據(jù)在一年內(nèi)增長(zhǎng)到了一億多條,系統(tǒng)響應(yīng)越來越慢,到后來連基本的查詢都要等幾十秒。更要命的是,由于合作周期長(zhǎng)、包含售后環(huán)節(jié),這些數(shù)據(jù)根本無法按時(shí)間歸檔。
當(dāng)我深入了解后發(fā)現(xiàn),這不是簡(jiǎn)單的數(shù)據(jù)量問題,而是系統(tǒng)設(shè)計(jì)出現(xiàn)了根本性偏移。
深入業(yè)務(wù)流程,尋找問題根源
面對(duì)這樣的問題,朋友的反應(yīng)是對(duì)系統(tǒng)進(jìn)行分庫(kù)分表,但我覺得這是治標(biāo)不治本。系統(tǒng)慢的根本原因在于設(shè)計(jì)的不合理,而不是數(shù)據(jù)量本身。
讓我先給大家看看他們的業(yè)務(wù)流程圖:
圖片
這個(gè)流程圖很清楚地展示了他們的業(yè)務(wù)復(fù)雜性。上游項(xiàng)目先發(fā)布生產(chǎn)計(jì)劃,供貨商根據(jù)計(jì)劃拆分采購(gòu)列表,聯(lián)系不同工廠協(xié)調(diào)預(yù)排期,然后是質(zhì)量審核、下單支付、確認(rèn)排期,工廠制定采購(gòu)材料計(jì)劃,分批生產(chǎn)制造,最后是驗(yàn)收和退換。
通過深入分析這個(gè)業(yè)務(wù)流程,我發(fā)現(xiàn)了一個(gè)關(guān)鍵問題:系統(tǒng)的核心已經(jīng)從原來的"訂單驅(qū)動(dòng)"轉(zhuǎn)變?yōu)?排期驅(qū)動(dòng)",但技術(shù)架構(gòu)卻沒有跟上這個(gè)變化。
原來的系統(tǒng)是圍繞訂單設(shè)計(jì)的,通過自動(dòng)匹配實(shí)現(xiàn)上下游分單。但加入排期功能后,整個(gè)業(yè)務(wù)流程變了:現(xiàn)在是先有排期協(xié)商,再根據(jù)排期生成訂單。可架構(gòu)還是以訂單為中心,這就導(dǎo)致了一個(gè)尷尬的局面——主訂單承擔(dān)了排期、生產(chǎn)、物流、財(cái)務(wù)等一大堆職責(zé),變成了一個(gè)"超級(jí)胖子"。
解構(gòu)現(xiàn)有系統(tǒng)架構(gòu)
為了更好地理解問題,我繪制了當(dāng)前系統(tǒng)的架構(gòu)圖:
圖片
從這張圖可以清楚地看到,多個(gè)角色都在使用這個(gè)"訂單排期系統(tǒng)",但訂單表承載的職能過多,導(dǎo)致多個(gè)流程依賴訂單表無法做數(shù)據(jù)維護(hù)。訂單存在多個(gè)和訂單業(yè)務(wù)無關(guān)的狀態(tài),比如排期周期很長(zhǎng),導(dǎo)致訂單一直不能關(guān)閉。
這種設(shè)計(jì)帶來了三個(gè)嚴(yán)重問題:
性能問題:?jiǎn)伪頂?shù)據(jù)量過大,查詢效率急劇下降。一個(gè)訂單表要承擔(dān)排期、生產(chǎn)、物流、財(cái)務(wù)等多重職責(zé),每次查詢都要處理大量不相關(guān)的數(shù)據(jù)。
維護(hù)性問題:一個(gè)實(shí)體承擔(dān)多重職責(zé),任何一個(gè)環(huán)節(jié)的變更都可能影響全局。比如修改排期邏輯可能會(huì)影響到訂單處理,修改物流狀態(tài)可能會(huì)影響到財(cái)務(wù)對(duì)賬。
擴(kuò)展性問題:新業(yè)務(wù)接入困難,開發(fā)效率低下。每次要增加新功能,都要考慮對(duì)現(xiàn)有復(fù)雜訂單模型的影響。
重新定義系統(tǒng)邊界
經(jīng)過仔細(xì)分析,我按照用戶角色重新梳理了整個(gè)業(yè)務(wù)流程:
圖片
這張圖按角色及其所需動(dòng)作畫出多個(gè)框,將他們需要做的動(dòng)作和數(shù)據(jù)流穿插起來,讓我們更清楚地看到了各個(gè)角色的職責(zé):
上游采購(gòu)方主要關(guān)注計(jì)劃發(fā)布和收貨驗(yàn)收,他們的核心動(dòng)作是發(fā)布進(jìn)貨計(jì)劃、收貨排期、下單、收貨/退換。
供貨商負(fù)責(zé)協(xié)調(diào)排期和訂單服務(wù),主要做的是排期分單、協(xié)調(diào)工廠、提供訂單相關(guān)服務(wù)。
工廠則專注于生產(chǎn)排期和產(chǎn)品制造,負(fù)責(zé)生產(chǎn)排期、產(chǎn)品制造、售后服務(wù)。
通過這樣的梳理,我們發(fā)現(xiàn)整個(gè)流程可以明確分為三個(gè)階段:
第一階段是計(jì)劃排期協(xié)調(diào)階段,這個(gè)階段不涉及訂單,主要是上游和多個(gè)工廠的排期協(xié)商。
第二階段是生產(chǎn)供貨交付階段,基于確認(rèn)排期生成訂單,執(zhí)行生產(chǎn)和物流。
第三階段是售后服務(wù)調(diào)換階段,處理訂單完成后的服務(wù)和問題。
系統(tǒng)拆分方案設(shè)計(jì)
基于這個(gè)發(fā)現(xiàn),我設(shè)計(jì)了新的系統(tǒng)架構(gòu):
圖片
我們決定將系統(tǒng)拆分為兩個(gè)子系統(tǒng):排期調(diào)度系統(tǒng)和訂單交付系統(tǒng)。
排期調(diào)度系統(tǒng)專門處理第一階段的工作。上游在這里提交進(jìn)貨計(jì)劃和收貨排期,供貨商與工廠協(xié)商分單和議價(jià),多方達(dá)成一致后進(jìn)行排期預(yù)占。這個(gè)系統(tǒng)的核心是"協(xié)商"和"調(diào)度",不涉及具體的交易行為。
核心功能包括進(jìn)貨計(jì)劃管理、排期協(xié)商調(diào)度、產(chǎn)能預(yù)約管理、排期狀態(tài)跟蹤。數(shù)據(jù)模型包括schedule_plan(排期計(jì)劃表)、capacity_booking(產(chǎn)能預(yù)約表)、supplier_coordination(供貨商協(xié)調(diào)表)、schedule_status(排期狀態(tài)表)。
訂單交付系統(tǒng)則處理后兩個(gè)階段的工作。當(dāng)排期確認(rèn)后,系統(tǒng)會(huì)自動(dòng)在訂單系統(tǒng)中生成對(duì)應(yīng)的訂單,工廠根據(jù)排期進(jìn)行生產(chǎn),分批次發(fā)貨,處理售后等等。這個(gè)系統(tǒng)的核心是"執(zhí)行"和"交付"。
核心功能包括訂單生命周期管理、生產(chǎn)執(zhí)行跟蹤、物流配送管理、財(cái)務(wù)對(duì)賬結(jié)算。數(shù)據(jù)模型包括delivery_order(交付訂單表)、production_batch(生產(chǎn)批次表)、logistics_tracking(物流跟蹤表)、financial_settlement(財(cái)務(wù)結(jié)算表)。
兩個(gè)系統(tǒng)通過排期ID和批次號(hào)進(jìn)行關(guān)聯(lián),使用異步消息機(jī)制處理跨系統(tǒng)交互,并且有統(tǒng)一的主數(shù)據(jù)管理服務(wù)。
代碼架構(gòu)的重構(gòu)過程
讓我展示一下拆分前后的代碼結(jié)構(gòu)對(duì)比:
圖片
這是拆分前的代碼結(jié)構(gòu)。可以看到,單一訂單服務(wù)承載過多職責(zé),業(yè)務(wù)邏輯高度耦合,數(shù)據(jù)訪問層職責(zé)不清晰。
圖片
這是拆分后的代碼結(jié)構(gòu)。各系統(tǒng)職責(zé)邊界清晰,減少了跨角色的實(shí)體調(diào)用,大大提高了系統(tǒng)的可維護(hù)性。
在具體實(shí)現(xiàn)上,我們采用了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的思想,為每個(gè)系統(tǒng)定義了清晰的聚合根和業(yè)務(wù)邊界。
排期調(diào)度系統(tǒng)的核心實(shí)體是SchedulePlan(排期計(jì)劃),負(fù)責(zé)管理整個(gè)排期的生命周期:
@Entity
publicclassSchedulePlan {
private String scheduleId;
private String demandId;
private ScheduleStatus status;
private LocalDateTime planStartTime;
publicvoidconfirmSchedule(List<FactoryCapacity> capacities) {
validateCapacities(capacities);
this.status = ScheduleStatus.CONFIRMED;
publishScheduleConfirmedEvent();
}
}
當(dāng)排期確認(rèn)后,系統(tǒng)會(huì)發(fā)布一個(gè)領(lǐng)域事件,訂單交付系統(tǒng)監(jiān)聽這個(gè)事件,自動(dòng)創(chuàng)建對(duì)應(yīng)的訂單:
@EventHandler
public void handleScheduleConfirmed(ScheduleConfirmedEvent event) {
DeliveryOrder order = createDeliveryOrder(event);
ProductionPlan plan = generateProductionPlan(event);
order.startProduction(plan);
}
這種事件驅(qū)動(dòng)的架構(gòu)既保證了數(shù)據(jù)的一致性,又實(shí)現(xiàn)了系統(tǒng)間的解耦。即使其中一個(gè)系統(tǒng)出現(xiàn)問題,也不會(huì)影響到另一個(gè)系統(tǒng)的正常運(yùn)行。
拆分決策的科學(xué)方法
在拆分過程中,我們遵循了明確的拆分原則:
圖片
單一職責(zé)原則:每個(gè)數(shù)據(jù)實(shí)體只負(fù)責(zé)一個(gè)核心業(yè)務(wù)領(lǐng)域。訂單實(shí)體只管理訂單的完整生命周期,排期實(shí)體只負(fù)責(zé)排期協(xié)商和調(diào)度,避免實(shí)體承擔(dān)多重、交叉的業(yè)務(wù)職責(zé)。
業(yè)務(wù)流程內(nèi)聚原則:按業(yè)務(wù)流程的自然邊界進(jìn)行拆分。協(xié)調(diào)排期流程歸到排期調(diào)度系統(tǒng),生產(chǎn)執(zhí)行流程和售后服務(wù)流程歸到訂單交付系統(tǒng)。
數(shù)據(jù)依賴解耦原則:最小化跨系統(tǒng)的數(shù)據(jù)依賴,減少頻繁的JOIN操作,避免緊耦合的數(shù)據(jù)調(diào)用,每個(gè)系統(tǒng)維護(hù)完整的業(yè)務(wù)數(shù)據(jù)。
服務(wù)抽象的三重境界
在系統(tǒng)拆分的過程中,我們還遇到了一個(gè)重要問題:如何對(duì)底層服務(wù)進(jìn)行合理的抽象?經(jīng)過多年的實(shí)踐,我總結(jié)出了服務(wù)抽象的三種境界。
第一種是被動(dòng)抽象法,適合創(chuàng)業(yè)初期的團(tuán)隊(duì)。當(dāng)發(fā)現(xiàn)多個(gè)服務(wù)使用相同的業(yè)務(wù)邏輯時(shí),就將其抽象成公共服務(wù)。這種方式簡(jiǎn)單直接,但抽象程度不高,隨著業(yè)務(wù)的發(fā)展可能需要大規(guī)模重構(gòu)。
圖片
第二種是動(dòng)態(tài)輔助表方式,適合業(yè)務(wù)相對(duì)穩(wěn)定的中型團(tuán)隊(duì)。它的具體實(shí)現(xiàn)是這樣的:當(dāng)訂單系統(tǒng)被幾個(gè)開發(fā)小組共同使用,而不同業(yè)務(wù)創(chuàng)建的主訂單有不同的type,不同的type會(huì)將業(yè)務(wù)特性數(shù)據(jù)存儲(chǔ)在不同的輔助表內(nèi),比如普通商品保存在表order和表order_product_extra中,定制類商品的定制流程狀態(tài)保存在order_customize_extra中。
動(dòng)態(tài)輔助表可以讓實(shí)體保持穩(wěn)定,不同業(yè)務(wù)的特性數(shù)據(jù)存儲(chǔ)在不同的輔助表中。這種方式在查詢便利性和業(yè)務(wù)靈活性之間取得了平衡,但各業(yè)務(wù)之間的隔離性較差。
圖片
第三種是強(qiáng)制標(biāo)準(zhǔn)接口方式,適合大型企業(yè)的核心系統(tǒng)。底層服務(wù)只提供標(biāo)準(zhǔn)功能,業(yè)務(wù)的個(gè)性化部分完全由上層實(shí)現(xiàn)。這種方式的抽象程度最高,底層服務(wù)極其穩(wěn)定,但對(duì)上層業(yè)務(wù)的技術(shù)要求也更高。
圖片
在我們這個(gè)項(xiàng)目中,考慮到團(tuán)隊(duì)規(guī)模和業(yè)務(wù)特點(diǎn),我們選擇了第二種方式。既保證了核心訂單邏輯的穩(wěn)定性,又為不同類型的業(yè)務(wù)保留了足夠的靈活性。
改造效果令人驚喜
經(jīng)過三個(gè)月的努力,新系統(tǒng)終于上線了。效果超出了預(yù)期:
性能大幅提升:系統(tǒng)響應(yīng)時(shí)間從原來的幾十秒降低到了秒級(jí),數(shù)據(jù)庫(kù)查詢效率提升了90%,系統(tǒng)的整體吞吐量增加了5倍。
可維護(hù)性明顯改善:各個(gè)模塊職責(zé)清晰,開發(fā)團(tuán)隊(duì)可以并行工作,互不干擾。新功能的開發(fā)時(shí)間從原來的幾周縮短到幾天。
擴(kuò)展性得到保障:新的架構(gòu)為后續(xù)的業(yè)務(wù)發(fā)展奠定了堅(jiān)實(shí)的基礎(chǔ),可以靈活應(yīng)對(duì)各種業(yè)務(wù)變化。
但這個(gè)項(xiàng)目給我最大的啟發(fā)不是技術(shù)本身,而是對(duì)業(yè)務(wù)的深度理解。很多時(shí)候,我們遇到的所謂"技術(shù)問題",實(shí)際上都是業(yè)務(wù)問題在技術(shù)層面的體現(xiàn)。如果不能深入理解業(yè)務(wù)的本質(zhì),僅僅從技術(shù)角度出發(fā),往往只能治標(biāo)不治本。
系統(tǒng)拆分的方法論總結(jié)
圖片
回頭來看,這次改造的成功在于我們遵循了正確的方法論,我將其總結(jié)為"三步拆分法":
第一步:自上而下看流程。我們深入分析了業(yè)務(wù)流程,識(shí)別了關(guān)鍵的業(yè)務(wù)階段,確定了流程邊界和依賴關(guān)系。這為后續(xù)的系統(tǒng)拆分提供了清晰的指導(dǎo)。
第二步:自下而上看模塊。我們分析了數(shù)據(jù)實(shí)體的職責(zé),識(shí)別了數(shù)據(jù)依賴關(guān)系,確定了模塊拆分的邊界。這確保了拆分后的模塊具有良好的內(nèi)聚性和松耦合性。
第三步:綜合考慮做決策。我們平衡了流程完整性和模塊獨(dú)立性,考慮了團(tuán)隊(duì)組織結(jié)構(gòu),評(píng)估了技術(shù)實(shí)現(xiàn)的復(fù)雜度。這保證了方案的可行性和可維護(hù)性。
寫在最后的思考
技術(shù)的發(fā)展日新月異,但架構(gòu)設(shè)計(jì)的核心原則卻是相對(duì)穩(wěn)定的。無論是微服務(wù)、云原生,還是未來可能出現(xiàn)的新技術(shù),都離不開對(duì)業(yè)務(wù)的深度理解和對(duì)系統(tǒng)邊界的準(zhǔn)確劃分。
在這個(gè)快速變化的時(shí)代,我們既要保持對(duì)新技術(shù)的敏感度,也要堅(jiān)持對(duì)基礎(chǔ)原則的堅(jiān)守。記住一句話:沒有銀彈,只有最合適的解決方案。每一次架構(gòu)調(diào)整都是對(duì)業(yè)務(wù)理解的深化,也是技術(shù)能力的提升。
另一個(gè)重要的感悟是,系統(tǒng)架構(gòu)不是一成不變的。隨著業(yè)務(wù)的發(fā)展和市場(chǎng)的變化,原有的設(shè)計(jì)可能會(huì)逐漸偏離初衷。這就需要我們定期回顧和反思,及時(shí)調(diào)整架構(gòu),讓技術(shù)更好地服務(wù)于業(yè)務(wù)。