從技術(shù)債務(wù)到架構(gòu)升級(jí),滴滴國(guó)際化外賣的變革
背 景
商家營(yíng)銷簡(jiǎn)述
圖片
在外賣平臺(tái)的運(yùn)營(yíng)中,我們致力于通過(guò)靈活的補(bǔ)貼策略激勵(lì)商家,與商家共同打造良好的合作關(guān)系,也會(huì)提供多樣化的營(yíng)銷活動(dòng),幫助商家吸引更多用戶下單。通過(guò)這些活動(dòng),不僅能夠提高商家的銷量,還能讓用戶感受到實(shí)際的優(yōu)惠,從而增強(qiáng)他們對(duì)平臺(tái)的粘性。
前端技術(shù)特點(diǎn)
業(yè)務(wù)特點(diǎn):營(yíng)銷場(chǎng)景玩法多、活動(dòng)類型多、活動(dòng)鏈路長(zhǎng)、活動(dòng)規(guī)則復(fù)雜。
中后臺(tái)技術(shù)特點(diǎn):活動(dòng)配置表單規(guī)則多、聯(lián)動(dòng)復(fù)雜。
前臺(tái)技術(shù)特點(diǎn):終端類型多(PC/EXE/PAD/PHONE)、代碼重復(fù)度高、用戶輸入校驗(yàn)規(guī)則復(fù)雜。
業(yè)務(wù)技術(shù)架構(gòu)
圖片
活動(dòng)鏈路
圖片
商家營(yíng)銷相關(guān)項(xiàng)目迭代時(shí)間較長(zhǎng),積累的歷史技術(shù)債務(wù)越來(lái)越多,結(jié)合業(yè)務(wù)背景和以上的技術(shù)特點(diǎn),之前對(duì)歷史項(xiàng)目進(jìn)行過(guò)一波代碼治理,比如:多端代碼復(fù)用、后臺(tái)復(fù)雜度治理等。
2023年的治理更多解的是代碼重復(fù)及腐化相關(guān)的問(wèn)題,而外賣商家營(yíng)銷活動(dòng)的配置能力還處于非?;A(chǔ)的階段,2024年,我們結(jié)合業(yè)務(wù)新訴求的契機(jī),從系統(tǒng)架構(gòu)治理維度,對(duì)商家營(yíng)銷前端項(xiàng)目做了一個(gè)全面的升級(jí)。
現(xiàn)狀
外賣商家營(yíng)銷活動(dòng),按活動(dòng)來(lái)源區(qū)分主要有四大類:平臺(tái)招商、代運(yùn)營(yíng)、品牌代建、商戶自營(yíng)銷,按類型區(qū)分主要有四大類:特價(jià)菜、買贈(zèng)、免配、滿減。前后端底層區(qū)分這些活動(dòng)渠道及類型都是case by case的形式,以招商活動(dòng)創(chuàng)建為例:
不同活動(dòng)類型,優(yōu)惠信息都放在不同的字段里,特價(jià)菜是specialItemRule,免配是freeDeliveryRule,滿減是reductionRule,買贈(zèng)是buyGiftsRule,且規(guī)則rules字段層級(jí)嵌套冗余,字段屬性沒(méi)有規(guī)律可循,以下列舉了兩類活動(dòng)的部分字段。
特價(jià)菜活動(dòng)規(guī)則字段示例:
"specialItemRule": [{
"rulePurposeType": 0,
"rules": [{
"type": 3,
"content": {
"discountValueRange": null,
"discountValueList": [10, 11, 20],
"discountType": 1
}
}],
"selectItemNumRange": {
"min": 1,
"max": 10
},
"itemPromoRangeValue": {
"min": 1,
"max": 50
},
"picLimit": 1,
"priceLimit": null,
"itemType": 0,
"checkItemPriceDay": 7
}]
滿減規(guī)則字段示例:
"reductionRule": [{
"rulePurposeType": 0,
"rules": [{
"type": 1,
"content": {
"threshold": 10,
"discount": 5
}
}, {
"type": 1,
"content": {
"threshold": 20,
"discount": 8
}
}]
}]
面臨的挑戰(zhàn)
2024年隨著國(guó)際化外賣營(yíng)銷業(yè)務(wù)需求明顯增長(zhǎng),比如:需要從0到1搭建連鎖品牌商家自運(yùn)營(yíng)能力、拓展新的營(yíng)銷活動(dòng)類型(商家券),按照現(xiàn)有的架構(gòu)及配置能力來(lái)看,存在以下幾個(gè)問(wèn)題:
- 產(chǎn)品需求迭代支撐效率低:涉及通用字段,需要重復(fù)修改,特價(jià)菜+免配+滿減+買贈(zèng),4種活動(dòng)類型改4次,如果再算上活動(dòng)渠道修改,需要再翻倍,4種渠道??4類活動(dòng) = 16 次。
- 開(kāi)發(fā)遺漏:活動(dòng)鏈路長(zhǎng),以當(dāng)前最為復(fù)雜的招商活動(dòng)為例,從運(yùn)營(yíng)后臺(tái)配置招商計(jì)劃=>商戶前臺(tái)報(bào)名招商活動(dòng),是一個(gè)較長(zhǎng)的鏈路,由于系統(tǒng)數(shù)據(jù)模型不夠靈活,導(dǎo)致修改字段及UI展示時(shí)無(wú)規(guī)律可循,經(jīng)常需要梳理遺漏點(diǎn)。
- 可拓展性差:當(dāng)前架構(gòu)下,如果新增活動(dòng)類型,則涉及全鏈路所有接口改動(dòng),可復(fù)用性低。
由于業(yè)務(wù)發(fā)展的契機(jī),國(guó)際化外賣商家側(cè)需要新增一個(gè)連鎖品牌管理端,借助這個(gè)項(xiàng)目,我們進(jìn)行了商家營(yíng)銷架構(gòu)的升級(jí)。
解決方案
問(wèn)題分析
商家營(yíng)銷配置能力薄弱主要體現(xiàn)在底層數(shù)據(jù)結(jié)構(gòu)缺乏通用性和擴(kuò)展性。
從全局配置維度來(lái)看:
圖片
從數(shù)據(jù)結(jié)構(gòu)現(xiàn)狀來(lái)看:
圖片
- 同一類活動(dòng),在不同平臺(tái)(端、后臺(tái))數(shù)據(jù)結(jié)構(gòu)不一致。
- 同一類活動(dòng),在不同活動(dòng)來(lái)源場(chǎng)景下(自營(yíng)銷、招商、代運(yùn)營(yíng)、品牌),數(shù)據(jù)結(jié)構(gòu)不一致。
- 四類活動(dòng),活動(dòng)規(guī)則數(shù)據(jù)結(jié)構(gòu)不一致,創(chuàng)建需要case by case拼裝,詳情需要case by case渲染。
- 差異化分支共有:自營(yíng)銷創(chuàng)建4 + 招商報(bào)名4 + 招商計(jì)劃4 + 代運(yùn)營(yíng)1 + 品牌4 = 17。
整體思路
架構(gòu)治理最重要的一環(huán)就是設(shè)計(jì)出一個(gè)統(tǒng)一的活動(dòng)數(shù)據(jù)模型,涵蓋所有平臺(tái)和活動(dòng)來(lái)源場(chǎng)景。
圖片
- 抽象活動(dòng)實(shí)體信息
- 統(tǒng)一差異化配置
- 按活動(dòng)信息維度拆分組織字段,而不是按業(yè)務(wù)維度拆分(收斂類型、來(lái)源)。
- 按通用字段概括優(yōu)惠類型,而不是按業(yè)務(wù)概念枚舉(收斂規(guī)則)。
- 支持靈活拓展
項(xiàng)目成果
在抽象出活動(dòng)配置模型后,為保證后續(xù)需求或者人員變更能夠按照規(guī)范持續(xù)迭代,通過(guò)對(duì)應(yīng)的配置模型的API文檔,配套前端JSON Schema校驗(yàn)工具,約束后續(xù)拓展。
底層數(shù)據(jù)結(jié)構(gòu)完成治理后,在新項(xiàng)目中,我們也對(duì)配置表單方案進(jìn)行了優(yōu)化,使得項(xiàng)目的數(shù)據(jù)流轉(zhuǎn)更加清晰,確保數(shù)據(jù)的一致性和可靠性。
而在前后端交互層面,對(duì)接口字段進(jìn)行了運(yùn)行時(shí)校驗(yàn),做到了接口安全約束,避免因數(shù)據(jù)缺陷而導(dǎo)致的前端錯(cuò)誤。
數(shù)據(jù)結(jié)構(gòu)對(duì)比
新版活動(dòng)數(shù)據(jù)結(jié)構(gòu)是一個(gè)面向?qū)ο蟮脑O(shè)計(jì)架構(gòu),采用組合式領(lǐng)域模型設(shè)計(jì),通過(guò)策略模式實(shí)現(xiàn)業(yè)務(wù)規(guī)則的動(dòng)態(tài)裝配。
基礎(chǔ)活動(dòng)模型(ActInfoModel)可以被視為一個(gè)父類或者超類,定義了通用的屬性和行為,而其子類(如自營(yíng)銷活動(dòng)模型selfOpsModel)繼承了基礎(chǔ)特性,并可以實(shí)現(xiàn)或者重寫(xiě)一些特定的功能,以滿足不同渠道的具體需求。
當(dāng)出現(xiàn)新的渠道或者活動(dòng)類型時(shí),只需要?jiǎng)?chuàng)建新的子類,遵循現(xiàn)有的父類結(jié)構(gòu)。而基礎(chǔ)模型的修改也不會(huì)影響所有子類,只需要確保子類能夠適應(yīng)父類的接口變化即可。
圖片
配置表單方案優(yōu)化
在之前的項(xiàng)目里,表單間的組件通信,是傳統(tǒng)多層組件的數(shù)據(jù)傳遞形式,通過(guò)父子組件層層傳遞。
圖片
數(shù)據(jù)流:自上而下,每個(gè)組件都需要通過(guò)props接受和傳遞數(shù)據(jù)。
缺點(diǎn):增加了代碼復(fù)雜性,每個(gè)組件都需要顯式傳遞數(shù)據(jù),容易出現(xiàn)冗余代碼和數(shù)據(jù)同步問(wèn)題。
這種形式對(duì)于簡(jiǎn)單的表單場(chǎng)景來(lái)說(shuō),比較直觀,但是對(duì)于商家營(yíng)銷活動(dòng)配置場(chǎng)景來(lái)說(shuō),在過(guò)往需求迭代中出現(xiàn)了維護(hù)困難和數(shù)據(jù)同步異常的問(wèn)題,在新項(xiàng)目里,我們使用了配置模型+依賴注入的表單方案。
圖片
數(shù)據(jù)流: 數(shù)據(jù)通過(guò)依賴注入在組件樹(shù)的各層之間傳遞,子組件直接獲取所需數(shù)據(jù)
優(yōu)點(diǎn): 降低了組件之間的耦合性,減少了多層傳遞的冗余性,數(shù)據(jù)更加集中且易于管理
數(shù)據(jù)流轉(zhuǎn)對(duì)比
圖片
使用配置模型 + 依賴注入的方式不僅可以簡(jiǎn)化數(shù)據(jù)流轉(zhuǎn),還能實(shí)現(xiàn)集中管理,減少代碼冗余,提高數(shù)據(jù)一致性,更容易進(jìn)行維護(hù)和調(diào)試,特別是在需要?jiǎng)討B(tài)配置或復(fù)雜業(yè)務(wù)邏輯的場(chǎng)景下表現(xiàn)尤為突出。
接口安全保障
當(dāng)前數(shù)據(jù)安全問(wèn)題
為了避免接口數(shù)據(jù)異常,導(dǎo)致前端頁(yè)面白屏,我們通常會(huì)在代碼中加一些字段兜底邏輯,這樣帶來(lái)的問(wèn)題:
- 冗余的兜底邏輯:在組件中,使用“||“操作符、可選鏈和解構(gòu)默認(rèn)值等方式進(jìn)行兜底處理,導(dǎo)致同樣的邏輯在多個(gè)地方反復(fù)出現(xiàn)。
- 復(fù)雜的數(shù)據(jù)結(jié)構(gòu)處理:對(duì)于復(fù)雜的數(shù)據(jù)結(jié)構(gòu),通常為了某個(gè)字段兜底會(huì)出現(xiàn)一大坨繁瑣的代碼,影響代碼可讀性與代碼效率。
- 數(shù)據(jù)類型安全問(wèn)題:常規(guī)兜底形式無(wú)法保證數(shù)據(jù)類型安全,可能造成不符合預(yù)期的類型錯(cuò)誤,進(jìn)而引發(fā)應(yīng)用程序中的邏輯錯(cuò)誤或頁(yè)面崩潰。
在抽象出活動(dòng)配置模型后,活動(dòng)配置的定義是由標(biāo)準(zhǔn)的JSON Schema描述組成的,在這個(gè)基礎(chǔ)上,我們定義一些校驗(yàn)及默認(rèn)填充規(guī)則,并引入集中式的兜底機(jī)制,在接口數(shù)據(jù)返回時(shí),調(diào)用一個(gè)校驗(yàn)工具函數(shù),實(shí)現(xiàn)統(tǒng)一的兜底策略。校驗(yàn)工具函數(shù)是借助zod這個(gè)工具庫(kù)去實(shí)現(xiàn)的。
招商活動(dòng)配置描述示例
// 招商活動(dòng)規(guī)則
export const SignUpActRuleSchema = ActRuleSchema.extend({
selectNumRange: z
.object({
min: z.number().default(0),
max: z.number().default(0),
})
.default({ min: 0, max: 0 }),
actType: z.union([z.number(), z.string()]).default(0),
rule: z.array(SignUpRuleSchema).default([]),
})
export type SignUpActRule = z.infer<typeof SignUpActRuleSchema>
// 招商活動(dòng)詳情頁(yè)接口信息
export const SignUpDetailSchema = z.object({
actRule: SignUpActRuleSchema.default({}),
actInfo: SignUpInfoSchema.default({}),
shopJoinInfo: z.array(ShopJoinInfoSchema).default([]),
})
接口返回處理示例:
// 招商活動(dòng)詳情接口
// useApiSchema是統(tǒng)一的返回?cái)?shù)據(jù)校驗(yàn)工具函數(shù)
export async function getSignUpDetail(params: object = {}): Promise<SignUpDetail> {
const response = await post(GET_SIGN_UP_DETAIL, params, { returnData: false })
return useApiSchema<SignUpDetail>(SignUpDetailSchema, response.data, response.traceId)
}
useApiSchema函數(shù)功能包含:數(shù)據(jù)校驗(yàn)、兜底數(shù)據(jù)填充、埋點(diǎn)上報(bào)。
接口返回字段中若出現(xiàn)返回?cái)?shù)據(jù)類型錯(cuò)誤或者未返回的情況,將返回自定義的默認(rèn)值從而保障頁(yè)面正常展示,對(duì)于錯(cuò)誤數(shù)據(jù)也做了埋點(diǎn)上報(bào),當(dāng)?shù)竭_(dá)一定閾值時(shí)會(huì)進(jìn)行報(bào)警。
效率提升
日常迭代
活動(dòng)配置模型通過(guò)字段的抽象和整合,大幅提升了字段擴(kuò)展的效率。原本因各活動(dòng)類型和場(chǎng)景的數(shù)據(jù)結(jié)構(gòu)差異,需要在多處修改數(shù)據(jù)結(jié)構(gòu)和組件邏輯的場(chǎng)景,現(xiàn)在只需在一處進(jìn)行修改即可,大大提高了開(kāi)發(fā)效率。
以前臺(tái)項(xiàng)目活動(dòng)規(guī)則相關(guān)迭代為例:
圖片
開(kāi)發(fā)實(shí)例
以近期需求為例,我們需要新增一種券活動(dòng)類型,通過(guò)采用活動(dòng)配置模型和集中式狀態(tài)管理的開(kāi)發(fā)形式,使得開(kāi)發(fā)過(guò)程中對(duì)于數(shù)據(jù)相關(guān)的處理邏輯與狀態(tài)管理要比之前簡(jiǎn)易很多,開(kāi)發(fā)效率提升約40%。
后續(xù)規(guī)劃
以上架構(gòu)治理都是針對(duì)新的項(xiàng)目去做的實(shí)踐,而對(duì)于國(guó)際化外賣商家營(yíng)銷前端的其他項(xiàng)目同樣需要做架構(gòu)升級(jí)改造,后續(xù)我們計(jì)劃收斂運(yùn)營(yíng)后臺(tái)的活動(dòng)配置,將最為復(fù)雜的招商活動(dòng)鏈路進(jìn)行標(biāo)準(zhǔn)化,后臺(tái)配置=>前臺(tái)應(yīng)用,引用同一套數(shù)據(jù)模型。
國(guó)際化外賣商家營(yíng)銷前端架構(gòu)預(yù)期
圖片