新聞中心
1 前言
組件化開(kāi)發(fā)是一種利用可重用的軟件構(gòu)件來(lái)設(shè)計(jì)和開(kāi)發(fā)計(jì)算機(jī)系統(tǒng)的過(guò)程。借助組件化開(kāi)發(fā)可以實(shí)現(xiàn)最小化、高效交付。

平臺(tái)基礎(chǔ)體驗(yàn)部將業(yè)務(wù)邏輯抽象為組件,通過(guò)組合組件快速構(gòu)建商品Feed流,研發(fā)效率整體提升2倍。組件化開(kāi)發(fā)不僅帶來(lái)效率的提升,同時(shí)極大地增加了代碼復(fù)用性、降低了系統(tǒng)的復(fù)雜性等等。本文將詳細(xì)介紹組件化開(kāi)發(fā)的落地過(guò)程,為大家揭曉轉(zhuǎn)轉(zhuǎn)App快速迭代的奧秘。
2 背景
平臺(tái)基礎(chǔ)體驗(yàn)部主要承接轉(zhuǎn)轉(zhuǎn)App、小程序迭代。
2021年底,基于轉(zhuǎn)轉(zhuǎn)定位于“有質(zhì)檢、放心買(mǎi)賣(mài)的二手交易平臺(tái)”的背景,對(duì)首頁(yè)Feed(Feed,即:商品列表頁(yè))、主搜Feed整體改造。排期時(shí),我們發(fā)現(xiàn)商品卡片渲染投入耗時(shí)過(guò)多,無(wú)法完成春節(jié)前交付。短期解決方案,可以額外投入人力,按期交付。但考慮未來(lái)還會(huì)整體改版,需要尋求一種方式,能夠快速承接全平臺(tái)范圍產(chǎn)品迭代。
2022年3月,我們著手策劃新方案,并構(gòu)建出一套組件化開(kāi)發(fā)流程。
2022年9月底,距離轉(zhuǎn)轉(zhuǎn)集團(tuán)宣布品牌升級(jí)還有一個(gè)月的時(shí)間,App、小程序需要整體換新,其中僅Feed功能點(diǎn),升級(jí)多達(dá)20幾處。組件化開(kāi)發(fā)借此契機(jī),開(kāi)始大范圍應(yīng)用,效果得到了印證。
圖1 Feed改版UI示例
3 現(xiàn)狀分析
3.1 對(duì)接流程與開(kāi)發(fā)流程
需求正式評(píng)審后,進(jìn)入接口評(píng)審階段,日常對(duì)接流程如下圖所示。
圖2 對(duì)接流程
開(kāi)發(fā)同學(xué)小張,根據(jù)App首頁(yè)UI圖例定字段后,同步給native以及QA。隨后著手另一功能點(diǎn):App收藏夾推薦,定字段、同步字段。
接口評(píng)審后,進(jìn)入開(kāi)發(fā),開(kāi)發(fā)流程如下圖所示。
圖3 開(kāi)發(fā)流程
RD小張開(kāi)發(fā)首頁(yè)時(shí),首先獲取各業(yè)務(wù)的RPC數(shù)據(jù),然后自上而下寫(xiě)好數(shù)據(jù)渲染邏輯。完成自測(cè)后,提供給native、QA測(cè)試環(huán)境。緊接著,投入到收藏夾功能點(diǎn)開(kāi)發(fā),流程類(lèi)似。
看似十分順暢的流程,在執(zhí)行過(guò)程中并不順利,總會(huì)有一些“小插曲”,比如:
(1)UI中出現(xiàn)了橫排卡片樣式,卡片上多了一個(gè)心智元素,native小劉要求增加一個(gè)userText字段。
(2)同一個(gè)UI在小程序上卻要使用不同的商品圖尺寸,F(xiàn)E小方大喊:給我返回16×16的尺寸!
(3)小張忙得不亦樂(lè)乎,小趙過(guò)來(lái)支援,給商品圖字段定義為infoImage,與此前的image字段命名不同,QA小張、native小李很生氣,表示我不能接受,字段不統(tǒng)一難于維護(hù)。
圖4 對(duì)接流程中的小插曲
對(duì)接流程問(wèn)題多多,開(kāi)發(fā)流程當(dāng)然也沒(méi)有想象的那么順利。PM會(huì)提出一些臨時(shí)要求:在某些功能點(diǎn)上增加AB實(shí)驗(yàn),或者調(diào)整某處功能點(diǎn)的展示邏輯。RD在實(shí)現(xiàn)過(guò)程中可能會(huì)調(diào)整元素的渲染順序,進(jìn)而影響全局?jǐn)?shù)據(jù)渲染正確性,為了避免代碼調(diào)整引入額外的缺陷,QA的測(cè)試的用例也會(huì)變多。
圖5 開(kāi)發(fā)流程中的小插曲
3.2 項(xiàng)目進(jìn)展
進(jìn)入開(kāi)發(fā)前,RD基于現(xiàn)有的改動(dòng)點(diǎn)做出技術(shù)評(píng)估,輸出排期。以App首頁(yè)、App收藏夾推薦為例,從產(chǎn)品規(guī)則上看,只有RPC數(shù)據(jù)源不同,RD認(rèn)為大部分邏輯是可以復(fù)用的。所以,功能相近的位置可能會(huì)適當(dāng)?shù)臏p少排期。
但實(shí)際開(kāi)發(fā)中,新的代碼邏輯與歷史邏輯存在沖突,無(wú)法順利的融入到數(shù)據(jù)渲染代碼塊。各接口的實(shí)體類(lèi)定義五花八門(mén),復(fù)用某些數(shù)據(jù)渲染方法時(shí),存在很多實(shí)體之間的數(shù)據(jù)轉(zhuǎn)換,代碼顯得十分臃腫。很多無(wú)法預(yù)知的“小插曲”導(dǎo)致原本答應(yīng)產(chǎn)品在10個(gè)工作日內(nèi)完成20個(gè)Feed改造,即使加班也無(wú)法按時(shí)交付。其他職能的進(jìn)度也會(huì)受到影響,給整個(gè)項(xiàng)目帶來(lái)風(fēng)險(xiǎn)。
圖6 項(xiàng)目進(jìn)展
3.3 問(wèn)題匯總
回顧對(duì)接、開(kāi)發(fā)流程,主要暴露以下幾個(gè)問(wèn)題:
- 版本、終端、UI差異增加了代碼的復(fù)雜性,難以復(fù)用:RD為了適配各端不同的樣式,或者兼容native產(chǎn)生的線上問(wèn)題,或者為了兼容不同版本的樣式,往往要寫(xiě)很多控制邏輯,使得代碼邏輯越來(lái)越臃腫,復(fù)雜性與維護(hù)成本逐漸增加。
- 接口返回值字段不統(tǒng)一:因?yàn)殚_(kāi)發(fā)人員的命名習(xí)慣不一致,隨著接口的增多、產(chǎn)品的不斷迭代,可能會(huì)出現(xiàn)多個(gè)字段描述同一個(gè)功能點(diǎn)的情況。接口字段數(shù)量成爆炸式增長(zhǎng),難以維護(hù)。出現(xiàn)問(wèn)題時(shí),RD很難快速定位字段,溝通成本、認(rèn)知成本也會(huì)大幅增加。
- 無(wú)法適應(yīng)產(chǎn)品快速迭代:AB方案的加入、產(chǎn)品邏輯的調(diào)整使得元素的渲染邏輯過(guò)于龐大,元素之間的依賴(lài)關(guān)系變得更加復(fù)雜,RD無(wú)法快速交付。
- 工作內(nèi)容重復(fù),排期長(zhǎng),易引入延期風(fēng)險(xiǎn):很多功能點(diǎn)十分相似,但是開(kāi)發(fā)中無(wú)法完美復(fù)用,使得排期較長(zhǎng)。過(guò)長(zhǎng)的排期外加無(wú)法預(yù)知的隱藏問(wèn)題,很有可能帶來(lái)延期風(fēng)險(xiǎn)。
3.4 問(wèn)題切入點(diǎn)與解決方案
- 切入點(diǎn)
(1)RPC模塊、數(shù)據(jù)渲染邏輯中,有些內(nèi)容完全一致,如果將他們劃分為模塊,將大幅增加代碼的復(fù)用性。
(2)數(shù)據(jù)渲染數(shù)據(jù)是過(guò)程化的、自上而下的,如果能夠打破這種串行開(kāi)發(fā)模式,各數(shù)據(jù)渲染邏輯之間的耦合性大幅降低。
圖7 流程分析圖示-1
(3)深入觀察單一數(shù)據(jù)渲染內(nèi)部,如果將版本兼容、終端差異、AB實(shí)驗(yàn)等從數(shù)據(jù)渲染邏輯中剝離,只留下核心邏輯,數(shù)據(jù)模渲染更具復(fù)用性。
(4)不同元素的數(shù)據(jù)渲染邏輯之間難免會(huì)存在依賴(lài)關(guān)系,需要有一種資源編排機(jī)制,能夠?qū)⒏鲾?shù)據(jù)渲染邏輯按規(guī)則組織起來(lái)。
圖8 流程分析圖示-2
- 解決方案
結(jié)合相關(guān)的技術(shù)調(diào)研以及業(yè)界相關(guān)的經(jīng)驗(yàn),我們引入了《組件化開(kāi)發(fā)》,即:基于組件的開(kāi)發(fā)。組件、組件化開(kāi)發(fā)帶來(lái)的優(yōu)勢(shì)能夠達(dá)成我們的訴求,組件、組件化開(kāi)發(fā)具體是什么,具有什么優(yōu)勢(shì)?接下來(lái)將具體展開(kāi)描述。
4 走進(jìn)組件化開(kāi)發(fā)
4.1 組件定義
組件是一組功能相關(guān)的邏輯、數(shù)據(jù)的聚合。
組件是自包含和完備的,通常以一組完備的API形式開(kāi)放給使用者,使用者不需要了解組件內(nèi)部的邏輯,只需要關(guān)注API的使用,組件的實(shí)現(xiàn)邏輯和依賴(lài)由組件自己負(fù)責(zé)。
4.2 組件分類(lèi)
- 按職能可將組件分為:技術(shù)組件和業(yè)務(wù)組件。業(yè)務(wù)組件是把一組相關(guān)的業(yè)務(wù)邏輯封裝為一個(gè)組件,也稱(chēng)為管理邏輯組件。
- 按層次可以分為:框架組件、平臺(tái)組件、通用管理邏輯組件、領(lǐng)域管理邏輯組件、行業(yè)管理邏輯組件、個(gè)性化管理邏輯組件等。
- 按來(lái)源可以分為:開(kāi)源組件、商業(yè)組件、自研組件等。
4.3 組件模型
組件需要遵守組件模型協(xié)議。組件模型是一組標(biāo)準(zhǔn)。維多利亞大學(xué)電子與計(jì)算機(jī)工程系給出的組件模型如下圖所示。
圖9 組件模型的基礎(chǔ)元素
組件模型包括:接口列表、命名、元數(shù)據(jù)、互通性、自定義組件接口、組合接口、組件演進(jìn)、打包與部署。
4.4 組件特點(diǎn)
一個(gè)設(shè)計(jì)良好的組件應(yīng)該具備如下幾個(gè)特點(diǎn):
- 可管理:組件是基于統(tǒng)一的模型進(jìn)行設(shè)計(jì)和實(shí)現(xiàn),遵循統(tǒng)一的技術(shù)規(guī)范,由統(tǒng)一的元數(shù)據(jù)進(jìn)行描述,有清晰的分類(lèi)和分層,可由組件工具進(jìn)行統(tǒng)一管理,以規(guī)范一致的模式進(jìn)行使用。
- 可復(fù)用:可復(fù)用是組件的一個(gè)核心特點(diǎn)。通常組件都是經(jīng)過(guò)良好的設(shè)計(jì)和封裝的,可以被多個(gè)場(chǎng)景復(fù)用。只有能夠復(fù)用,才能更好地發(fā)揮組件的價(jià)值。
- 可配置:為了更好地復(fù)用,組件在不同的場(chǎng)景下使用時(shí)不需要去修改組件自身,通常組件需要把不同場(chǎng)景下可能會(huì)變化的部分作為可變參數(shù),允許使用者通過(guò)配置不同的值,來(lái)滿(mǎn)足不同使用場(chǎng)景下的需求,使組件具有更好的可復(fù)用性??膳渲玫膬?nèi)容通常包括環(huán)境信息、參數(shù)、規(guī)則、模型屬性等。
- 可擴(kuò)展:在使用組件的過(guò)程中,當(dāng)通過(guò)配置也無(wú)法滿(mǎn)足場(chǎng)景化的使用需求時(shí),組件的可擴(kuò)展性就變得尤其重要,如果一個(gè)組件不具備可擴(kuò)展性,將極大降低組件的復(fù)用價(jià)值。組件的擴(kuò)展性通??梢酝ㄟ^(guò)良好的設(shè)計(jì)來(lái)實(shí)現(xiàn),如支持繼承,可局部邏輯重寫(xiě);支持事件,通過(guò)前置后置事件,允許使用定制規(guī)則和邏輯,就可以更好地滿(mǎn)足不同場(chǎng)景下的個(gè)性化使用需求,提高組件的可復(fù)用性,發(fā)揮出組件更大的價(jià)值。
4.5 組件化開(kāi)發(fā)
組件化開(kāi)發(fā): 組件化開(kāi)發(fā),即:基于組件的開(kāi)發(fā)(Component-Based Development,簡(jiǎn)稱(chēng):CBD),是一種利用可重用的軟件構(gòu)件(組件)來(lái)設(shè)計(jì)和開(kāi)發(fā)計(jì)算機(jī)系統(tǒng)的過(guò)程。
組件化開(kāi)發(fā)的起源: 組件化開(kāi)發(fā)出現(xiàn)于20世紀(jì)90年代末。當(dāng)時(shí)面向?qū)ο蠼i_(kāi)發(fā)(Object-Oriented,簡(jiǎn)稱(chēng):OO)沒(méi)有像最初建議的那樣被廣泛的重用。OO產(chǎn)生了大量細(xì)粒度的類(lèi)、對(duì)象和關(guān)系,在這些較小的單元中發(fā)現(xiàn)可重用的部件是非常困難的。CBD背后的思想是集成相關(guān)的部分并對(duì)它們進(jìn)行集體重用。
4.6 組件化開(kāi)發(fā)的類(lèi)型
組件化開(kāi)發(fā)分為兩類(lèi):機(jī)會(huì)式重用、帶有開(kāi)發(fā)量的重用。
- 機(jī)會(huì)式重用:根據(jù)已有的組件組合成一套系統(tǒng)。
圖10 機(jī)會(huì)式重用
- 帶有開(kāi)發(fā)量的重用:根據(jù)需求,定制開(kāi)發(fā)某些組件,然后將組件組合成一套系統(tǒng)。
圖11 帶有開(kāi)發(fā)量的重用
在日常的使用場(chǎng)景中,因?yàn)楫a(chǎn)品不斷地迭代,使用現(xiàn)有組件直接組合成系統(tǒng)的場(chǎng)景較少,帶有開(kāi)發(fā)量的組件重用應(yīng)用場(chǎng)景更為廣泛。
4.7 組件化開(kāi)發(fā)的優(yōu)勢(shì)
- 最小化交付:基于現(xiàn)有的組件,即可裝配成系統(tǒng)。
- 高效:開(kāi)發(fā)人員可以更集中關(guān)注需求變更點(diǎn),快速完成產(chǎn)品升級(jí)。
- 提升系統(tǒng)質(zhì)量:開(kāi)發(fā)人員可以有更多的時(shí)間來(lái)確保系統(tǒng)質(zhì)量。組件的高質(zhì)量決定了系統(tǒng)的質(zhì)量。
- 減少支出:減少資源投入。
5 組件化開(kāi)發(fā)的落地過(guò)程
借助組件化開(kāi)發(fā)思想,我們構(gòu)建了一套組件化開(kāi)發(fā)架構(gòu),內(nèi)部稱(chēng)之為:星環(huán),整體架構(gòu)如下圖所示。
圖12 星環(huán)框架-組件化開(kāi)發(fā)整體架構(gòu)圖
星環(huán)體系可以總結(jié)為兩層一包:聲明層、驅(qū)動(dòng)層以及業(yè)務(wù)組件包。
- 聲明層:包含基礎(chǔ)聲明、RPC模塊聲明、組件聲明、降級(jí)觸發(fā)規(guī)則聲明?;A(chǔ)聲明以Java代碼形式聲明,剩余部分以XML形式聲明。
(1)基礎(chǔ)聲明:定義了應(yīng)用名、應(yīng)用上下文、請(qǐng)求參數(shù)、組件的驅(qū)動(dòng)方式等。
(2)RPC模塊聲明:定義了應(yīng)用中包含哪些RPC引用,RPC模塊之間的依賴(lài)關(guān)系。
(3)組件聲明:定義了應(yīng)用中包含哪些業(yè)務(wù)組件、組件的屬性、組件的觸發(fā)條件。
(4)降級(jí)觸發(fā)規(guī)則聲明:定義了入口層的監(jiān)控策略、降級(jí)觸發(fā)規(guī)則、監(jiān)控頻率等。
- 驅(qū)動(dòng)層:負(fù)責(zé)XML配置文件解析加載,RPC/組件的資源編排與執(zhí)行。其中:
(1)RPC驅(qū)動(dòng)層:即復(fù)仇者框架,一種自研的基于事件驅(qū)動(dòng)的并發(fā)調(diào)度模型(了解更多,可查看參考文獻(xiàn)第5點(diǎn)),負(fù)責(zé)解析RPC配置聲明,編排RPC資源,執(zhí)行RPC調(diào)用。
(2)組件驅(qū)動(dòng)層:負(fù)責(zé)組件配置解析,組件命中判定,組件的資源編排以及提供組件驅(qū)動(dòng)入口。
- 業(yè)務(wù)組件包:由業(yè)務(wù)邏輯組件構(gòu)成的資源包,以jar形式管理。包含:標(biāo)題組件、商品圖組件、到手價(jià)組件等。
此外,還有周邊生態(tài)為之賦能,包括:健康監(jiān)測(cè)、輔助生態(tài)。
- 健康監(jiān)測(cè):檢測(cè)入口層服務(wù)健康度。當(dāng)指定時(shí)間窗口內(nèi)錯(cuò)誤率達(dá)到上限,觸發(fā)熔斷與降級(jí)。
- 輔助生態(tài):輔助開(kāi)發(fā)人員快速構(gòu)建組件應(yīng)用。包含IDEA XML配置自動(dòng)補(bǔ)全提示、maven腳手架輔助生成代碼框架等。
整個(gè)架構(gòu)是如何逐步構(gòu)建起來(lái)的,構(gòu)建過(guò)程中有哪些需要解決的問(wèn)題,下面分模塊一一介紹。
5.1 前置工作
5.1.1 統(tǒng)一組件口徑
首先我們要確定什么是組件,在前文中提到:組件是一組功能相關(guān)的邏輯、數(shù)據(jù)的聚合。其中,數(shù)據(jù)可以理解為接口中的每一個(gè)字段,邏輯即為每個(gè)字段所對(duì)應(yīng)的產(chǎn)品規(guī)則。為什么把每一個(gè)字段定義為一個(gè)組件呢?這與我們的工作職能密切相關(guān),與中臺(tái)后端不同,平臺(tái)后端是服務(wù)于前端的后端,即:BFF(Backend For Frontend)。BFF的主要職責(zé)是組合、使用底層數(shù)據(jù),然后按照產(chǎn)品規(guī)則處理展示邏輯,最后返回給前端。
日常對(duì)接中,后端根據(jù)UI要求定義接口,并根據(jù)各終端特性調(diào)整接口協(xié)議。每個(gè)UI元素對(duì)應(yīng)一組產(chǎn)品規(guī)則,每個(gè)UI元素對(duì)應(yīng)一個(gè)字段。字段又作為前后端數(shù)據(jù)交互的橋梁,把每個(gè)字段劃歸為組件最合適不過(guò)。
圖13 日常對(duì)接流程
圖14 BFF開(kāi)發(fā)模式
5.1.2 推進(jìn)標(biāo)準(zhǔn)化
構(gòu)成組件的元素是一組標(biāo)準(zhǔn)。標(biāo)準(zhǔn)的建立有助于實(shí)現(xiàn)組件的復(fù)用性。前文中,我們把字段定義為組件,字段、組件、UI三者之間一一對(duì)應(yīng)。推進(jìn)組件的標(biāo)準(zhǔn)化,也就是對(duì)字段、UI的標(biāo)準(zhǔn)化。
(1)字段命名標(biāo)準(zhǔn)化
因?yàn)殚_(kāi)發(fā)人員的命名習(xí)慣不一致,可能會(huì)出現(xiàn)多個(gè)字段描述同一個(gè)功能點(diǎn)的情況。當(dāng)以字段維度劃分不同的組件后,組件會(huì)變得非常冗余,在接口對(duì)接時(shí)也會(huì)增加溝通成本。另外,非標(biāo)準(zhǔn)化字段對(duì)native的組件化實(shí)現(xiàn)不是很友好,nanative需要額外維護(hù)非標(biāo)字段數(shù)據(jù)與UI元素的映射,增加了開(kāi)發(fā)與維護(hù)成本。
圖15 native組件化方案
(2)UI樣式標(biāo)準(zhǔn)化
雖然在不同終端、在不同頁(yè)面中UI呈現(xiàn)出個(gè)性化差異。但有些元素背后對(duì)應(yīng)的產(chǎn)品邏輯是一致的。例如:商品卡片中的官方驗(yàn)成色標(biāo)簽傳達(dá)的內(nèi)容一致,在首頁(yè)、主搜可以統(tǒng)一樣式。從產(chǎn)品角度上看,標(biāo)準(zhǔn)化的UI可以在全平臺(tái)營(yíng)造統(tǒng)一的用戶(hù)體驗(yàn)氛圍;在技術(shù)方面,不僅降低了維護(hù)API的成本,同時(shí)native根據(jù)標(biāo)準(zhǔn)化的UI可以構(gòu)建出UI組件,進(jìn)而豐富組件庫(kù),提升復(fù)用性。
圖16 不同F(xiàn)eed的共同元素
5.1.3 規(guī)范接口返回值結(jié)構(gòu)
接口返回值中包含著前端展示的各種數(shù)據(jù),隨著產(chǎn)品的迭代,UI中的元素類(lèi)型會(huì)越來(lái)越多,開(kāi)發(fā)人員習(xí)慣性地不斷在接口返回值中平鋪新增的字段。時(shí)間周期拉長(zhǎng),字段數(shù)量成爆炸性增張,難以維護(hù)。因此,返回值結(jié)構(gòu)也需要有一套標(biāo)準(zhǔn),抑制字段數(shù)量增長(zhǎng)速度。
(1)按對(duì)象內(nèi)容類(lèi)型收攏字段
以商品卡中的元素為例,頁(yè)面上有很多標(biāo)簽元素:官方驗(yàn)成色標(biāo)簽、功能描述標(biāo)簽、埋點(diǎn)標(biāo)簽等,這些字段在表現(xiàn)形式上都為純圖片或者純文字的形式,可以統(tǒng)一歸納為標(biāo)簽對(duì)象。
圖17 按對(duì)象內(nèi)容類(lèi)型收攏字段
(2)按功能合并字段
在不同的終端上,圖片鏈接有時(shí)要拼接屬性參數(shù)。以往針對(duì)圖片是否攜帶寬高屬性,我們會(huì)返回兩個(gè)字段picUrl、picUrlWH,現(xiàn)在統(tǒng)一為:picUrl。是否拼接寬高參數(shù)作為組件屬性移動(dòng)到組件中。
(3)融入KV結(jié)構(gòu)
KV結(jié)構(gòu)用來(lái)存儲(chǔ)一些非標(biāo)字段,如:埋點(diǎn)字段postIdMap,包含上報(bào)的埋點(diǎn)透?jìng)髯侄巍?/p>
(4)支持返回值VO擴(kuò)展
暫時(shí)無(wú)法確認(rèn)以后是否被定義為標(biāo)準(zhǔn)化字段,先放在VO的子類(lèi)VOExt中。VO中只包含標(biāo)準(zhǔn)化字段聲明。
圖18 返回值VO類(lèi)圖
5.2 組件粒度
獲取組件的執(zhí)行結(jié)果經(jīng)歷兩個(gè)步驟:執(zhí)行RPC調(diào)用,然后依據(jù)產(chǎn)品邏輯進(jìn)行數(shù)據(jù)加工。以實(shí)際使用場(chǎng)景為例:
渲染標(biāo)題時(shí):調(diào)用商品服務(wù),獲取商品基礎(chǔ)信息。
渲染到手價(jià)時(shí):調(diào)用商品服務(wù),獲取商品基礎(chǔ)信息;調(diào)用促銷(xiāo)服務(wù),獲取活動(dòng)促銷(xiāo)信息。
渲染劃線價(jià)時(shí):調(diào)用促銷(xiāo)服務(wù),獲取活動(dòng)促銷(xiāo)信息。
匯總RPC調(diào)用與數(shù)據(jù)渲染的關(guān)系,如下圖所示。
圖19 RPC調(diào)用與數(shù)據(jù)渲染邏輯關(guān)系圖
RPC調(diào)用與數(shù)據(jù)渲染邏輯呈現(xiàn)多對(duì)多的關(guān)系。再次回看組件的定義,組件是一組功能相關(guān)的邏輯、數(shù)據(jù)的聚合。功能邏輯該怎樣去定義呢?若組件的功能邏輯定義為RPC調(diào)用與數(shù)據(jù)渲染邏輯的組合,會(huì)引發(fā)以下問(wèn)題:
(1)RPC重復(fù)調(diào)用:多個(gè)組件中可能包含同一RPC數(shù)據(jù)源,每個(gè)組件獲取一次RPC數(shù)據(jù),重復(fù)調(diào)用,對(duì)下游造成壓力。
(2)組件職責(zé)不單一,難以復(fù)用:若合并多個(gè)組件,只調(diào)用一次RPC,那么組件的職責(zé)不再單一,復(fù)用性降低。
(3)難于資源編排:組件間存在依賴(lài)關(guān)系、RPC模塊間也存在依賴(lài)關(guān)系,且RPC執(zhí)行順序與組件的執(zhí)行順序沒(méi)有必然聯(lián)系,當(dāng)RPC與數(shù)據(jù)渲染綁定在一起時(shí),難于資源編排。
綜上,RPC調(diào)用要獨(dú)立于數(shù)據(jù)渲染邏輯,組件的功能邏輯只包含數(shù)據(jù)渲染。
進(jìn)一步的,當(dāng)RPC調(diào)用從組件中分離后,需要為組件提供獲取RPC數(shù)據(jù)的方式??梢栽趹?yīng)用上下文中提供屬性域SynchronizedMap存儲(chǔ)RPC結(jié)果集,為二者建立通信橋梁。
圖20 RPC/數(shù)據(jù)渲染間的數(shù)據(jù)交互
5.3 組件類(lèi)設(shè)計(jì)
依據(jù)組件組件遵循的組件模型協(xié)議,定義組件類(lèi)包含以下內(nèi)容:
- 元數(shù)據(jù)
組件類(lèi)型(componentType):標(biāo)識(shí)當(dāng)前組件隸屬的功能點(diǎn)。例如:標(biāo)題組件、商品圖組件、到手價(jià)組件等等。
依賴(lài)的組件列表(dependencyComponentTypeList):標(biāo)識(shí)當(dāng)前組件依賴(lài)于哪些業(yè)務(wù)組件基礎(chǔ)數(shù)據(jù)。例如:《券信息》數(shù)據(jù)的有無(wú)依賴(lài)于《到手價(jià)》的數(shù)據(jù)情況。
以上屬性定義在組件頂級(jí)接口中,組件頂級(jí)接口類(lèi)結(jié)構(gòu)如下所示。
圖21 組件頂級(jí)接口
- 行為定義
通用行為doHandle():定義了組件的標(biāo)準(zhǔn)行為,該行為定義在組件頂級(jí)接口中。
- 自定義組件接口
自定義行為doInnerHandle():定義了組件的自定義行為。與doHandle()的區(qū)別是:doHandle()中定義的是組件行為的通用執(zhí)行邏輯,doInnerHandle()用于實(shí)現(xiàn)各組件的個(gè)性化業(yè)務(wù)邏輯。該行為定義在自定義組件的頂級(jí)類(lèi)中,自定義組件的頂級(jí)類(lèi)結(jié)構(gòu)如下圖所示。
圖22 自定義組件的頂級(jí)類(lèi)
基礎(chǔ)組件類(lèi)定義好之后,還需要根據(jù)業(yè)務(wù)場(chǎng)景定義:定義父組件,新建業(yè)務(wù)組件。匯總類(lèi)圖如下圖所示。
圖23 組件類(lèi)圖
- 父組件類(lèi)
自定義組件的頂級(jí)類(lèi)中沒(méi)有聲明具體的返回值VO類(lèi)型。在實(shí)際使用時(shí),需要依據(jù)場(chǎng)景聲明返回值類(lèi)型,主鍵類(lèi)型,參數(shù)類(lèi)型等,這些內(nèi)容聲明在父組件類(lèi)中。比如在Feed流商品卡片場(chǎng)景,新建FeedComponentHandleService,指定主鍵類(lèi)型為L(zhǎng)ong類(lèi)型的商品infoId、指定返回值類(lèi)型為FeedBaseInfoVOExt等。
- 業(yè)務(wù)組件類(lèi)
在父組件類(lèi)的基礎(chǔ)上,可以新建業(yè)務(wù)組件類(lèi),比如:標(biāo)題組件TitleV1、商品圖組件ImageV1、商品圖組件ImageV2...
5.4 參數(shù)傳遞
為了提升RPC模塊、組件的復(fù)用性,在其中會(huì)增加可變參數(shù),允許使用者在不同場(chǎng)景配置不同的值。RPC/組件參數(shù)值來(lái)自于接口參數(shù),當(dāng)直接使用接口請(qǐng)求參數(shù)作為RPC/組件的參數(shù)時(shí),因接口請(qǐng)求參數(shù)屬性域 = {RPC模塊參數(shù)屬性域,組件參數(shù)屬性域,其他參數(shù)},RPC/組件參數(shù)中新增了很多無(wú)用參數(shù)屬性。另一方面接口參數(shù)類(lèi)型多樣,同一組件難以適配不同場(chǎng)景中,復(fù)用性大幅降低。所以RPC/組件應(yīng)該只關(guān)注自身需要使用的參數(shù)。
圖24 參數(shù)設(shè)置
接口請(qǐng)求參數(shù)與RPC/組件參數(shù)獨(dú)立管理后,當(dāng)外界請(qǐng)求到來(lái)時(shí),需要RPC/組件建立獲取接口參數(shù)的通信方式,RPC/組件獲取參數(shù)屬性值的過(guò)程如下圖所示。
圖25 獲取RPC/組件參數(shù)屬性值的過(guò)程
當(dāng)獲取RPC/組件獲取參數(shù)屬性值時(shí),首先獲取到RPC/組件請(qǐng)求的代理對(duì)象,然后由MethodInterceptor攔截代理對(duì)象的方法,轉(zhuǎn)而執(zhí)行原始請(qǐng)求對(duì)象中的方法。
實(shí)現(xiàn)思路:
(1)定義IRequestFiledAutoMapped接口,屬性域中含有一個(gè)ThreadLocal對(duì)象用于存儲(chǔ)原始請(qǐng)求對(duì)象。RPC/組件請(qǐng)求類(lèi)均實(shí)現(xiàn)該接口。
(2)定義攔截器MethodInterceptor,用于攔截IRequestFiledAutoMapped各實(shí)現(xiàn)類(lèi)的方法請(qǐng)求。
(3)定義BeanProcessor,組件Bean生成后,使用Enhancer創(chuàng)建代理對(duì)象。
圖26 請(qǐng)求參數(shù)、自動(dòng)映射處理類(lèi)圖
5.5 組件命中條件
在傳統(tǒng)編程模式中,數(shù)據(jù)渲染中包含了大量的條件判斷語(yǔ)句,如圖所示。
圖27 耦合條件判斷的數(shù)據(jù)渲染代碼
不同的場(chǎng)景往往只會(huì)命中條件語(yǔ)句中的一個(gè)路徑。將這些條件判斷和產(chǎn)品功能邏輯剝離開(kāi),組件邏輯中只包含產(chǎn)品功能邏輯,將大幅提升組件的復(fù)用性。此外,條件的剝離可以讓功能代碼塊瘦身,開(kāi)發(fā)人員的學(xué)習(xí)、認(rèn)知成本將大幅減少。
回看上圖中的示例,將條件與產(chǎn)品功能邏輯分離后可以劃分為3個(gè)組件:
組件1:title = title + content
組件2:title = tinyTitle
組件3:title = title + paramsValue
劃分之后,可以根據(jù)不同的場(chǎng)景選用不同的組件,組件邏輯更易升級(jí)維護(hù)。
組件模型中規(guī)定了組合組件的接口、規(guī)則。組件的命中條件作為規(guī)則,與組件配合使用,用來(lái)判定某個(gè)場(chǎng)景應(yīng)該選用哪個(gè)組件。實(shí)現(xiàn)上,我們提供了兩種條件的聲明形式。
(1)提供一個(gè)條件頂級(jí)接口IBaseCondition,實(shí)現(xiàn)其中的eveluation()方法,在方法中聲明命中規(guī)則。
圖28 條件頂級(jí)接口
(2)使用EL表達(dá)式描述命中規(guī)則,由Aviator規(guī)則引擎加載規(guī)則。
如何綁定條件與組件,以及如何執(zhí)行命中條件,在5.6中將會(huì)展開(kāi)描述。
5.6 管理多版本組件
- 管理方式
各類(lèi)組件準(zhǔn)備完畢后,需要將這些組件組織起來(lái),如何組裝組件也需要遵循一套標(biāo)準(zhǔn)。以XML文件描述組件的裝配信息。使用這種聲明方式,各組裝點(diǎn)成塊狀結(jié)構(gòu),層次清晰,以下為XML的配置樣例。
圖29 demoApp1的組件配置樣例
配置文件主要包含四部分:應(yīng)用聲明、RPC模塊聲明、組件聲明、自定義參數(shù)聲明。各部分聲明的內(nèi)容在第5節(jié)的前置介紹中,不再贅述。
- 加載XML
轉(zhuǎn)轉(zhuǎn)的微服務(wù)由Spring Boot框架支撐,借助AbstractBeanDefinitionParser,重寫(xiě)其中的parseInternal方法,解析XML,生成單例模式Bean。Bean的結(jié)構(gòu)如下圖所示。
圖30 星環(huán)組件Bean結(jié)構(gòu)
- 再提組件命中條件
框架中提供了兩種組件條件接入方式:實(shí)現(xiàn)IBaseCondition接口或者使用EL表達(dá)式。在配置文件中,聲明樣例如下圖。
圖31 組件命中規(guī)則聲明示例
(1)若實(shí)現(xiàn)IBaseCondition接口,則完善conditionClass的屬性值,值為IBaseCondition接口實(shí)現(xiàn)類(lèi)的類(lèi)路徑。
(2)若使用EL表達(dá)式,則完善conditionEL的屬性值,值為EL表達(dá)式內(nèi)容。
- 組件命中
當(dāng)條件與功能邏輯分離后,功能邏輯會(huì)演變?yōu)槎鄠€(gè)組件。因?yàn)檎?qǐng)求場(chǎng)景是未知的、不確定的,將組件按照類(lèi)型收攏起來(lái)為:組件組,如下圖所示。
圖32 組件組配置結(jié)構(gòu)
在實(shí)際場(chǎng)景中,不同的場(chǎng)景只會(huì)命中其中的唯一一個(gè)組件,是否命中組件按照如下的規(guī)則觸發(fā):
(1)自上而下判定組件命中。命中任意組件,則完成該組件組的判定。
(2)優(yōu)先讀取conditionEL配置,規(guī)則引擎執(zhí)行結(jié)果返回true則命中該組件,否則不命中;如果配置了conditionClass類(lèi)路徑,執(zhí)行條件實(shí)例中的evaluate方法,若方法返回值為true,則命中該組件,否則不命中。
- 組件收集
當(dāng)完成某一組件組的結(jié)果判定,被命中的組件會(huì)被收集在集合中,即:Set
5.7 組件排序
收集好組件列表之后,還不能立即執(zhí)行這些組件的行為。在實(shí)際場(chǎng)景中,組件與組件之間存在數(shù)據(jù)依賴(lài),如下圖所示。
圖33 組件之間的依賴(lài)
《券標(biāo)簽》組件的展示條件依賴(lài)于《活動(dòng)信息》組件、《到手價(jià)》組件。所以在組件執(zhí)行順序上,需要將《活動(dòng)信息》、《到手價(jià)》組件執(zhí)行前置。底層實(shí)現(xiàn)上,根據(jù)組件間的依賴(lài)先后關(guān)系,排序組件。
組件之間的依賴(lài)關(guān)系可以主動(dòng)聲明也可通過(guò)自動(dòng)解析獲取到。排序?qū)崿F(xiàn)上,自動(dòng)解析的方式可通過(guò)三色標(biāo)記的逆向解析法實(shí)現(xiàn)組件編排(了解更多,可查看參考文獻(xiàn)第10點(diǎn),4.3.3章節(jié))。我們采取主動(dòng)聲明依賴(lài),有向圖拓?fù)渚幣诺姆绞健?/p>
具體為:
(1)編寫(xiě)組件時(shí),在組件內(nèi)部聲明本組件類(lèi)型、依賴(lài)的組件類(lèi)型。
圖34 聲明本組件類(lèi)型、依賴(lài)的組件類(lèi)型
(2)將每個(gè)組件看作為圖的一個(gè)節(jié)點(diǎn),依賴(lài)關(guān)系視為弧,生成一張有向圖,有向圖使用鄰接表存儲(chǔ)。
圖35 組件節(jié)點(diǎn)有向圖
圖36 組件節(jié)點(diǎn)鄰接表
(3)對(duì)有向圖進(jìn)行拓?fù)渑判?,?dāng)有向圖存在環(huán)狀結(jié)構(gòu)時(shí),日志提示存在互相引用;若有向圖無(wú)環(huán),則輸出最終的排序結(jié)果。
圖37 組件節(jié)點(diǎn)拓?fù)渑判?/p>
(4)按序執(zhí)行組件行為。
5.8 組件驅(qū)動(dòng)
經(jīng)過(guò)前述流程,組件模型構(gòu)造完成??蚣苄枰峁┤肟诠┩饨缯{(diào)用。以調(diào)用場(chǎng)景中的實(shí)體數(shù)劃分場(chǎng)景為:?jiǎn)我灰晥D、多視圖。
- 單一視圖:渲染單一同類(lèi)實(shí)體的數(shù)據(jù)。例如:某個(gè)商品卡片,某個(gè)商品詳情頁(yè)。
- 多視圖:渲染多個(gè)同類(lèi)實(shí)體的數(shù)據(jù)。例如:多個(gè)商品卡片,多個(gè)商品詳情頁(yè)。
對(duì)于每個(gè)場(chǎng)景,提供相應(yīng)的調(diào)用入口、自定義驅(qū)動(dòng)組件列表的方法,如下圖所示。
圖38 組件調(diào)用入口類(lèi)
單一視圖入口:renderView();多視圖入口:renderViewList()。視圖入口中提供自定義驅(qū)動(dòng)組件列表的方法。例如:在多視圖場(chǎng)景中通過(guò)實(shí)現(xiàn)driveCardView()方法,實(shí)現(xiàn)串行執(zhí)行組件或并行執(zhí)行組件。
5.9 新的開(kāi)發(fā)模式
使用組件化開(kāi)發(fā)方式,構(gòu)建feed流只需要以下幾個(gè)步驟:
(1)新建業(yè)務(wù)組件(可選):如果有新的業(yè)務(wù)邏輯,則新建業(yè)務(wù)組件類(lèi)。
(2)聲明配置:在XML配置中聲明應(yīng)用名、RPC模塊列表、組件列表。
(3)聲明驅(qū)動(dòng):定義應(yīng)用Bean、應(yīng)用上下文、請(qǐng)求參數(shù)、組件的驅(qū)動(dòng)方式等。
(4)執(zhí)行驅(qū)動(dòng):執(zhí)行命中的組件列表。
(5)額外的邏輯處理:埋點(diǎn)日志打印等。
(6)結(jié)果返回
6 效果數(shù)據(jù)
以我的頁(yè)面增加推薦Feed場(chǎng)景為例:
研發(fā)側(cè)效率整體提升:2.2倍。
衡定過(guò)程:組件化模式前工時(shí)投入÷組件化模式后工時(shí)投入。即:84H÷37H≈2.2。
研發(fā)側(cè)投入成本明細(xì)如下:
圖39 研發(fā)效率明細(xì)
7 輔助生態(tài)
- XML自動(dòng)補(bǔ)全插件
編寫(xiě)組件配置時(shí),經(jīng)常需要查看組件元信息:組件的屬性等內(nèi)容,影響編寫(xiě)效率?;贗ntelliJ Platform Plugin Template,開(kāi)發(fā)了XML自動(dòng)補(bǔ)全提示插件。
- maven腳手架(規(guī)劃中)
組件開(kāi)發(fā)模式,可以沉淀一套代碼結(jié)構(gòu)。腳手架生成通用代碼后,可進(jìn)一步較少開(kāi)發(fā)投入。
8 總結(jié)
- 傳統(tǒng)的開(kāi)發(fā)模式:受版本、終端、產(chǎn)品迭代等多因素影響,隨著時(shí)間的推移,代碼邏輯越來(lái)越復(fù)雜,維護(hù)成本高,復(fù)用性低,無(wú)法應(yīng)對(duì)快速的產(chǎn)品迭代。
- 組件化開(kāi)發(fā)模式:將功能邏輯凝練為組件,代碼更具復(fù)用性。組合組件即可完成系統(tǒng)的構(gòu)建,高效交付。
本文詳細(xì)講述了組件化開(kāi)發(fā)技術(shù)的實(shí)現(xiàn)過(guò)程,希望對(duì)大家有所幫助。歡迎大家在評(píng)論區(qū)留言,也可添加微信號(hào):zpc_1994,進(jìn)一步交流。
9 參考文獻(xiàn)
[1] 畢偉.組件化軟件開(kāi)發(fā)方法,2022,http://mm.chinapower.com.cn/zjqy/gddh/20221207/178571.html
[2] Margaret Rouse.Component-Based Development,2015,https://www.techopedia.com/definition/31002/component-based-development-cbd
[3] UNIC.Component-Based Development,2014,https://www.ece.uvic.ca/~itraore/seng422-06/notes/arch06-7-1.pdf
[4] 闞耀光.appview-auto-degrade-cache,2022.
[5] 陳奇恩.復(fù)雜并發(fā)場(chǎng)景下的并發(fā)調(diào)度模型在轉(zhuǎn)轉(zhuǎn)的演進(jìn)之路,2022,https://mp.weixin.qq.com/s/6o4hQokmRytrb0Hevzly0g
[6] 陸晨,致遠(yuǎn),陳琦.GraphQL及元數(shù)據(jù)驅(qū)動(dòng)架構(gòu)在后端BFF中的實(shí)踐,2021,https://mp.weixin.qq.com/s/mhM9tfWBlIuMVkZQ-6C0Tw
[7] Sam Newman.Backends For Frontends,2015,https://samnewman.io/patterns/architectural/bff/
[8] 谷金.通用商品卡片的設(shè)計(jì)過(guò)程,2022.
[9] 陸晨,致遠(yuǎn),陳琦.標(biāo)準(zhǔn)化思想及組裝式架構(gòu)在后端BFF中的實(shí)踐,2022,https://mp.weixin.qq.com/s/7VlXBl9syw2ppiR3x237bA
[10] 樂(lè)彬,國(guó)梁,玉龍.美團(tuán)外賣(mài)廣告平臺(tái)化的探索與實(shí)踐,2022,https://mp.weixin.qq.com/s/Iyd_uYkNI5cH2sv_VwT3NA
[11] 嚴(yán)蔚敏,吳偉民.數(shù)據(jù)結(jié)構(gòu)(C語(yǔ)言版)[M].北京:清華大學(xué)出版社,2007.163~183.
文章題目:提效新紀(jì)元-組件化開(kāi)發(fā)在轉(zhuǎn)轉(zhuǎn)App中的應(yīng)用-后端篇
路徑分享:http://m.fisionsoft.com.cn/article/djgipeg.html


咨詢(xún)
建站咨詢(xún)
