(資料圖片)
阿里妹導(dǎo)讀
本文主要講述我們?nèi)绾瓮ㄟ^一個(gè)主干業(yè)務(wù)流程承接多個(gè)業(yè)務(wù)場(chǎng)景并在數(shù)據(jù)上可適配到多端型多場(chǎng)景,實(shí)現(xiàn)在服務(wù)端高質(zhì)量高效率的“包接口”。
一、背景
前臺(tái)業(yè)務(wù)同學(xué)在業(yè)務(wù)承接過程中總是抱怨大部分業(yè)務(wù)無法通過設(shè)計(jì)模式來承接,寫的代碼是越來越?jīng)]有追求,理由是我無法預(yù)測(cè)未來的業(yè)務(wù)的發(fā)展,且設(shè)計(jì)模式更多的是在框架或中間件中使用。然而設(shè)計(jì)模式是對(duì)能力抽象出的通用模式,從哲學(xué)的角度來看世間萬物皆塵土,事物都是可以抽象出共同的本質(zhì)的東西。所以,難道只有底層能力可以抽象,業(yè)務(wù)邏輯部分就不可以抽象了?必須可以才是啊。
在前臺(tái)業(yè)務(wù)承接過程中除了能力可以抽象,還有可以抽象出業(yè)務(wù)流程,假設(shè)在有這樣一些業(yè)務(wù)場(chǎng)景,品搜和圖搜、直播間評(píng)論和點(diǎn)贊、公域直播會(huì)場(chǎng)和私域商詳透直播等等,這些各領(lǐng)域內(nèi)的業(yè)務(wù)流程“大同小異”,因此都可以抽象出通用的業(yè)務(wù)流程節(jié)點(diǎn)。
但是通常在一個(gè)主干流程需要承接的場(chǎng)景有很多,比如直播間互動(dòng)這個(gè)主干流程包括了直播間評(píng)論、點(diǎn)贊、求講解、看證書、進(jìn)場(chǎng)等等場(chǎng)景,所以我們需要通過主要流程進(jìn)行進(jìn)行多場(chǎng)景承接。但是這樣還不夠,在面對(duì)多端型多場(chǎng)景的情況下需要處理返回不同的數(shù)據(jù)模型。
綜上所述,我們?nèi)绾瓮ㄟ^一個(gè)主干業(yè)務(wù)流程承接多個(gè)業(yè)務(wù)場(chǎng)景并在數(shù)據(jù)上可適配到多端型多場(chǎng)景,實(shí)現(xiàn)在服務(wù)端高質(zhì)量高效率的“包接口”,下面會(huì)詳細(xì)介紹。
二、業(yè)務(wù)承接
如果你面臨的問題是在同一個(gè)業(yè)務(wù)域上承接多種類似的業(yè)務(wù)場(chǎng)景,每天在適配各種端型或者各種場(chǎng)景而對(duì)外提供接口時(shí),為了保證應(yīng)用系統(tǒng)的可擴(kuò)展性和高效承接業(yè)務(wù),那么可以按照如下步驟進(jìn)行設(shè)計(jì)。
2.1 業(yè)務(wù)流程抽象
首先需要進(jìn)行業(yè)務(wù)建模,抽象出用戶用例或者user story,當(dāng)然具體的粒度可以自己把控,具體如下:
2.1.1 用戶用例
在直播間互動(dòng)領(lǐng)域內(nèi)的用戶用例如下:
?
從整個(gè)系統(tǒng)出發(fā),挖掘出面向不同的用戶提供的能力有哪些,在這些用例背后需要進(jìn)行的流程和節(jié)點(diǎn)又是什么。通過這些流程和節(jié)點(diǎn)才能進(jìn)行后續(xù)的系統(tǒng)時(shí)序和流程抽象,舉例如下
在互動(dòng)領(lǐng)域內(nèi)的流程和節(jié)點(diǎn)如下:
風(fēng)控檢查
評(píng)論持久化
評(píng)論上屏
評(píng)論進(jìn)溝通
2.2.2 系統(tǒng)時(shí)序
基于用戶用例進(jìn)行分析,這些用例都需要經(jīng)過什么的流程節(jié)點(diǎn)進(jìn)行處理,然后將這些流程按照系統(tǒng)時(shí)序進(jìn)行呈現(xiàn)。
到此基于上述的用例和時(shí)序是不是可以抽象出具體互動(dòng)流程了,顯而易見。
2.2.3 業(yè)務(wù)流程抽象
有了系統(tǒng)用例和系統(tǒng)時(shí)序這一步就比較簡(jiǎn)單,從系統(tǒng)時(shí)序里很容易可以抽象出具體的流程和流程中的處理節(jié)點(diǎn),具體如下:
對(duì)于直播間互動(dòng)領(lǐng)域可以抽象出的業(yè)務(wù)流程:風(fēng)控檢查->互動(dòng)內(nèi)容持久化-》消息上屏-》互動(dòng)進(jìn)IM
對(duì)于直播間分發(fā)領(lǐng)域可以抽象出的業(yè)務(wù)流程:直播推薦-》直播間基礎(chǔ)信息-》直播流信息-》直播間品信息
到此,大家可以按照上述步驟在大腦里對(duì)自己的業(yè)務(wù)域進(jìn)行抽象出來了。
2.2.4 設(shè)計(jì)模式固化主流程
按照業(yè)務(wù)主流程可以通過模板模式將處理流程固定下來,如下所示:
@Override@LiveLog(logResult = true)public InteractionResult interactionSubmit(MobileInteractionRequest request, InteractionLiveRoom liveRoom) { Boolean needSave = MapUtils.getBoolean(request.getExtInfo(), LiveInteractionConstant.NEED_SAVE); // 默認(rèn)保存 InteractionResult saveResult = null; if (Objects.isNull(request.getExtInfo()) || Objects.isNull(needSave) || needSave) { saveResult = save(request, liveRoom); if(Objects.nonNull(saveResult) && !saveResult.isSuccess()) { return saveResult; } } // 默認(rèn)進(jìn)溝通 InteractionResult chatResult; if (Objects.isNull(request.getSendToChat()) || Boolean.parseBoolean(request.getSendToChat())) { chatResult = sendToChat(request); if(Objects.nonNull(chatResult) && !chatResult.isSuccess()) { return chatResult; } } if(Objects.nonNull(saveResult) && saveResult.isSuccess()) { return saveResult; } return null;}/** * 互動(dòng)行為保存到數(shù)據(jù)庫(kù)或者緩存中 * * @param request * @return */protected abstract InteractionResult save(MobileInteractionRequest request, InteractionLiveRoom liveRoom);/** * 進(jìn)溝通 * * @param request * @return */protected abstract InteractionResult sendToChat(MobileInteractionRequest request);2.2 業(yè)務(wù)流程擴(kuò)展
因在上述模版模式中預(yù)留了兩個(gè)擴(kuò)展點(diǎn),所以在子類中可以通過擴(kuò)展點(diǎn)進(jìn)行擴(kuò)展,舉例如下:?
?
如果有更多的場(chǎng)景就需要擴(kuò)展實(shí)現(xiàn)上述兩個(gè)擴(kuò)展點(diǎn)進(jìn)行擴(kuò)展即可,這樣保證了業(yè)務(wù)的高效承接。這里會(huì)有兩個(gè)問題:
在程序運(yùn)行時(shí)如何根據(jù)具體的場(chǎng)景選擇哪個(gè)子類進(jìn)行邏輯處理
如何進(jìn)行適配端和場(chǎng)景返回的數(shù)據(jù)模型
針對(duì)第一個(gè)問題,其實(shí)就是如何去if else的問題,這里也給出比較經(jīng)典的方案:
枚舉法
表驅(qū)動(dòng)法
策略模式+工廠模式
其中枚舉法和表驅(qū)動(dòng)法比較簡(jiǎn)單易用,原理就是將映射關(guān)系封裝在枚舉類或本地緩存中,這里簡(jiǎn)單介紹下如何通過策略模式消除if else。
// 策略接口public interface Opt { int apply(int a, int b);}// 策略實(shí)現(xiàn)類@Component(value = "addOpt")public class AddOpt implements Opt { @Autowired xxxAddResource resource; // 這里通過Spring框架注入了資源 @Override public int apply(int a, int b) { return resource.process(a, b); }}// 策略實(shí)現(xiàn)類@Component(value = "devideOpt")public class devideOpt implements Opt { @Autowired xxxDivResource resource; // 這里通過Spring框架注入了資源 @Override public int apply(int a, int b) { return resource.process(a, b); }}// 策略處理@Componentpublic class OptStrategyContext{ private Map總結(jié)偽代碼:
// 抽象類固定業(yè)務(wù)流程 預(yù)留擴(kuò)展點(diǎn)public abstract class AbstractXxxx { doXxx(Object context) { // 節(jié)點(diǎn)1 doNode1(context); // 節(jié)點(diǎn)2 doNode2(context); // 節(jié)點(diǎn)3 doNode3(context); // 節(jié)點(diǎn)n ... } // 擴(kuò)展點(diǎn)1 protected abstract Result doNode1(Object context); // 擴(kuò)展點(diǎn)2 protected abstract Result doNode2(Object context); // 擴(kuò)展點(diǎn)3 protected abstract Result doNode3(Object context);} // 策略處理public class OptStrategyContext{ private Map2.3 多場(chǎng)景多端型適配
上面我們只是通過模版模式抽象出了主干業(yè)務(wù)流程,但是如何適配不同的端型和不同的場(chǎng)景,返回不同的數(shù)據(jù)模型呢,這里有兩種答案,一種是模版模式、另一種是“棒棒糖”模式,下面逐一介紹。
2.3.1 模版模式適配
既然是模版模式,這里的主干流程又是什么呢?主要跟我們解決的問題有關(guān)系,按照2.1中的流程步驟,可以抽象出固定的流程為:請(qǐng)求入?yún)⑻幚?》業(yè)務(wù)邏輯處理-》結(jié)果返回處理。
其中業(yè)務(wù)邏輯處理可以選定為2.2中介紹的通過策略模式選擇業(yè)務(wù)擴(kuò)展的子類,來處里業(yè)務(wù)部分;請(qǐng)求入?yún)⒑徒Y(jié)果返回處理部分可以設(shè)置為擴(kuò)展點(diǎn),供子類擴(kuò)展。具體偽代碼如下:
// 抽象類固定業(yè)務(wù)流程 預(yù)留擴(kuò)展點(diǎn) 適配多端型多場(chǎng)景public abstract class AbstractSceneAdapter {注:因要適配不同端型不同場(chǎng)景返回不同的數(shù)據(jù)模型,所以上述偽代碼中主流程最終返回的結(jié)果是一個(gè)泛型,在子類實(shí)現(xiàn)的時(shí)候進(jìn)行確定具體返回的類型。
2.3.1 棒棒糖模式適配
通過模版模式來適配時(shí)會(huì)有一個(gè)小問題,當(dāng)需要有多個(gè)請(qǐng)求入?yún)⑻幚砥骰蛘叨鄠€(gè)結(jié)果包裝器的時(shí)候需要在模版里增加處理節(jié)點(diǎn),但其實(shí)這些節(jié)點(diǎn)是有共性的可抽象出來的。因此可以針對(duì)入?yún)⑻幚砥骱徒Y(jié)果包裝器定義單獨(dú)的接口,需要多個(gè)處理器時(shí)同時(shí)實(shí)現(xiàn)接口進(jìn)行處理。然后這些實(shí)現(xiàn)類打包放在單獨(dú)的類中依次執(zhí)行即可。當(dāng)然其中的業(yè)務(wù)處理部分也可以定義接口動(dòng)態(tài)實(shí)現(xiàn)。偽代碼如下:
// 入?yún)⑻幚砥鱬ublic interface IRequestFilter<> { void doFilter(T t);}// 結(jié)果包裝器public interface IResultWrapper三、接口設(shè)計(jì)
基于上述兩種設(shè)計(jì)模式來適配時(shí)我們的接口又該如何設(shè)計(jì),是設(shè)計(jì)面向通用的業(yè)務(wù)層接口還是面向定制化的業(yè)務(wù)接口,兩種方式各有優(yōu)缺點(diǎn):
優(yōu)點(diǎn) | 缺點(diǎn) | |
通用業(yè)務(wù)接口 | 1,擴(kuò)展性強(qiáng) 2,業(yè)務(wù)承接效率高,無需頻繁發(fā)布代碼 | 1,業(yè)務(wù)邊界不清晰,問題排查效率低 2,接口數(shù)據(jù)冗余 |
定制化業(yè)務(wù)接口 | 1,業(yè)務(wù)邊界清晰 2,接口出入?yún)?shù)據(jù)無冗余 | 1,無擴(kuò)展 2,頻繁包接口發(fā)布代碼 |
對(duì)于接口提供者來說肯定不希望頻繁改動(dòng)代碼發(fā)布代碼,但是又希望能夠在業(yè)務(wù)承接過程中能夠高效適配多端型多場(chǎng)景,因此這里總結(jié)了下接口設(shè)計(jì)原則:
1、對(duì)于越底層的接口應(yīng)該越通用,例如HSF接口、領(lǐng)域服務(wù)、中間件提供的接口;
2、對(duì)于越上層的接口應(yīng)該越定制化,例如對(duì)于不同的UI適配、不同的場(chǎng)景適配等;
3、對(duì)于業(yè)務(wù)領(lǐng)域內(nèi)的接口應(yīng)該通用化,例如直播業(yè)務(wù)域的分發(fā)領(lǐng)域、互動(dòng)領(lǐng)域內(nèi)的接口盡可能的通用化;
四、總結(jié)
在承接業(yè)務(wù)過程中會(huì)面臨頻繁包接口、一個(gè)view層的數(shù)據(jù)模型充滿了小100個(gè)屬性,系統(tǒng)的擴(kuò)展性遇到瓶頸,這些問題除了通過平臺(tái)化配置化的能力來解決,但是回歸到代碼本身我們?nèi)稳豢梢酝ㄟ^抽象的設(shè)計(jì)模式來解決。
基于抽象的理論達(dá)到復(fù)用、高內(nèi)聚低耦合,降低系統(tǒng)復(fù)雜度的目標(biāo),設(shè)計(jì)模式不只是用在底層能力或中間件中,在業(yè)務(wù)承接過程中亦有大的功效。
千萬不要為了用設(shè)計(jì)模式而刻意使用設(shè)計(jì)模式,帶來的效果適得其反,在選擇設(shè)計(jì)模式時(shí)也要三思,落地后再改動(dòng)成本將會(huì)巨大。
在前臺(tái)業(yè)務(wù)開發(fā)中,需要?jiǎng)澐种鞲鱾€(gè)業(yè)務(wù)領(lǐng)域,在領(lǐng)域中抽象出該業(yè)務(wù)的處理流程,基于流程可設(shè)計(jì)相關(guān)的擴(kuò)展和編排能力,方式有很多種,包括SPI、設(shè)計(jì)模式、DSL等,本文主要通過模版模式和棒棒糖模式來解決問題。
接口設(shè)計(jì)應(yīng)該按照越底層越通用,越上層越定制化的原則進(jìn)行設(shè)計(jì),當(dāng)然在業(yè)務(wù)域內(nèi)的接口應(yīng)盡可能的通用話。
阿里云開發(fā)者社區(qū),千萬開發(fā)者的選擇
阿里云開發(fā)者社區(qū),百萬精品技術(shù)內(nèi)容、千節(jié)免費(fèi)系統(tǒng)課程、豐富的體驗(yàn)場(chǎng)景、活躍的社群活動(dòng)、行業(yè)專家分享交流,歡迎點(diǎn)擊【閱讀原文】加入我們。
營(yíng)業(yè)執(zhí)照公示信息