成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

美團外賣Android平臺化架構(gòu)演進實踐

移動開發(fā) Android
美團外賣自 2013 年創(chuàng)建以來,業(yè)務一直高速發(fā)展。目前美團外賣日完成訂單量已突破 1800 萬,成為美團點評最重要的業(yè)務之一。

美團外賣自 2013 年創(chuàng)建以來,業(yè)務一直高速發(fā)展。目前美團外賣日完成訂單量已突破 1800 萬,成為美團點評最重要的業(yè)務之一。

[[224117]]

美團外賣的用戶端入口,從單一的外賣獨立 App,拓展為外賣、美團、點評等多個 App 入口。

美團外賣所承載的業(yè)務,也從單一的餐飲業(yè)務,發(fā)展到餐飲、超市、生鮮、果蔬、藥品、鮮花、蛋糕、跑腿等十多個大品類業(yè)務。業(yè)務的快速發(fā)展對客戶端架構(gòu)不斷提出新的挑戰(zhàn)。

平臺化背景

很早之前,外賣作為孵化中的項目只有美團外賣 App(下文簡稱外賣 App)一個入口,后來外賣作為一個子頻道接入到美團 App(下文簡稱外賣頻道),兩端業(yè)務并行迭代開發(fā)。

早期為了快速上線,開發(fā)同學直接將外賣 App 的代碼拷貝一份到外賣頻道,做了簡單的適配就很快接入到美團 App 了。

早期的外賣 App 和外賣頻道由兩個團隊分別維護,而在隨后一段時間里,兩端代碼體系差異越來越大。

最后演變成了從網(wǎng)絡、圖片等基礎(chǔ)庫到 UI 控件、類的命名等都不盡相同的兩套代碼。

盡管后來兩個團隊合并到一起,但歷史的差異已經(jīng)形成,為了優(yōu)先滿足業(yè)務需求,很長一段時間內(nèi),我們只能在兩套代碼的基礎(chǔ)上不斷堆積更多的功能。

維護兩套代碼的成本可想而知,而業(yè)務的迅猛發(fā)展又使得這一問題越發(fā)不可忍受。

在我們探索解決兩端代碼復用的同時,業(yè)務的發(fā)展又對我們提出新的挑戰(zhàn)。隨著團隊成員擴充了數(shù)倍,商超生鮮等垂直品類的拆分,以及異地研發(fā)團隊的建立,外賣客戶端的平臺化被提上日程。

而在此之前,外賣 App 和外賣頻道基本保持單工程開發(fā),這樣的模式顯然是無法支持多團隊協(xié)作開發(fā)的。

因此,我們需要快速將代碼重構(gòu)為支持平臺化的多工程模式,同時還要考慮業(yè)務模塊的解耦,使得新業(yè)務可以拷貝現(xiàn)有的代碼快速上線。

此外,在實施平臺化的過程中,兩端代碼復用的問題還沒有解決,如果兩端的代碼沒有統(tǒng)一而直接做平臺化業(yè)務拆庫,必然會導致問題的復雜化。

在這樣的背景下,可以看出我們面臨的問題相較于其他平臺型 App 更為特殊和復雜:既要解決外賣業(yè)務平臺化的問題,又要解決外賣 App 和外賣頻道兩端代碼復用的問題。

屢次探索

在實施平臺化和兩端代碼復用的道路上并非一帆風順,很多方案只有在嘗試之后才知道問題所在。

我們多次遇到這樣的情況:設計方案完成后,團隊已經(jīng)全身心投入到開發(fā)之中,但是由于業(yè)務形態(tài)發(fā)生變化,原有的設計也被迫更改。

在不斷的探索和實踐過程中,我們經(jīng)歷了多個中間階段。雖然有不少失敗的案例,但是也積累了很多架構(gòu)設計上的寶貴經(jīng)驗,整個團隊對業(yè)務和架構(gòu)也有了更深的理解。

搜索庫拆分實踐

早期美團外賣 App 和美團外賣頻道兩個團隊的合并,帶來的最大痛點是代碼復用,而非平臺化,而在很長的一段時間內(nèi),我們也沒有想過從平臺化的角度去解決兩端代碼復用的問題。

然而代碼復用的一些失敗嘗試,給后續(xù)平臺化的架構(gòu)帶來了不少寶貴的經(jīng)驗。

當時是怎么解決代碼復用問題的呢?我們通過和產(chǎn)品、設計同學的溝通,約定了未來的需求,會從需求內(nèi)容、交互、樣式上,兩端盡可能的保持一致。

經(jīng)過多次討論后,團隊發(fā)起了兩端代碼復用的技術(shù)方案嘗試,我們決定將搜索模塊從主工程拆分出來,并實現(xiàn)兩端代碼復用。

然而兩端的搜索模塊代碼底層差異很大,BaseActivity 和 BaseFragment 不統(tǒng)一,UI 樣式不統(tǒng)一,數(shù)據(jù) Model 不統(tǒng)一,圖片、網(wǎng)絡、埋點不統(tǒng)一,并且兩端發(fā)版周期也不一致。

針對這些問題的解決方案是:

  • 通過代理屏蔽 Activity 和 Fragment 基類不統(tǒng)一的問題。
  • 兩端主工程 style 覆蓋搜索庫的 UI 樣式。
  • 搜索庫使用獨立的數(shù)據(jù) Model,上層去做數(shù)據(jù)適配。
  • 其他差異通通拋出接口讓上層實現(xiàn)。
  • 和 PM 溝通盡量使產(chǎn)品需求和發(fā)版周期一致。

大致架構(gòu)如下圖:

雖然搜索庫在短期內(nèi)拆分為獨立的工程,并實現(xiàn)了絕大部分的兩端代碼復用。

但是好景不長,僅僅更新過幾個版本后,由于需求和版本發(fā)布周期的差異,搜索庫開始變?yōu)閮蓚€分支,并且兩個分支的差異越來越大,最后代碼無法合并而不得不永久維護兩個搜索庫。

搜索庫事實上是一次失敗的拆分,其中的問題總結(jié)起來有三個:

  • 在兩端底層差異巨大的情況下自上而下的強行拆分,導致大量實現(xiàn)和適配留在了兩端主工程實現(xiàn),這樣的設計層級混亂,邊界模糊,并且極大的增加了業(yè)務開發(fā)的復雜性。
  • 寄希望于兩端需求和發(fā)版周期完全一致這個想法不切實際,如果在架構(gòu)上不為兩端的差異性預留可伸縮的空間,復用最終是難以持續(xù)的。
  • 約定或規(guī)范,受限于組織架構(gòu)和具體執(zhí)行的個人,不確定性太高。

頁面組件化實踐

在經(jīng)歷過搜索庫的失敗拆分后,大家認為目前還不具備實現(xiàn)模塊整體拆分和復用的條件,因此我們走向了另一個方向,即實現(xiàn)頁面的組件化以達成部分組件復用的目標。

頁面組件化的設計思路是:

  • 將頁面拆分為粒度更小的組件,組件內(nèi)部除了包含 UI 實現(xiàn),還包含數(shù)據(jù)層和邏輯層。
  • 組件提供個性化配置滿足兩端差異需求,如果無法滿足再通過代理拋到上層處理。

頁面組件化是一個良好的設計,但它主要適用于解決 Activity 巨大化的問題。

由于底層差異巨大的情況,使得頁面組件化很難實現(xiàn)大規(guī)模的復用,復用效率低。另一方面,頁面組件化也沒有為兩端差異性預留可伸縮的空間。

MVP 分層復用實踐

我們還嘗試過運用設計模式解決兩端代碼復用的問題。想法是將代碼分為易變的和穩(wěn)定的兩部分,易變部分在兩端上層實現(xiàn)差異化處理,穩(wěn)定部分可以在下層實現(xiàn)復用。

方案的主要設計思路是:

  • 借鑒 Clean MVP 架構(gòu),根據(jù)職責將代碼拆分為 Presenter,Data Repository,Use Case,View,Model 等角色。
  • UI、動畫、數(shù)據(jù)請求等邏輯在下層僅保留接口,在上層實現(xiàn)并注入到下層。
  • 對于兩端不一致的數(shù)據(jù) Model,通過轉(zhuǎn)換器適配為下層統(tǒng)一的模型。

大致架構(gòu)如下圖:

這是一種靈活、優(yōu)雅的設計,能夠?qū)崿F(xiàn)部分代碼的復用,并能解決兩端基礎(chǔ)庫和 UI 等差異。

這個方案在首頁和二級頻道頁的部分模塊使用了一段時間,但是因為學習成本較高等原因推廣比較緩慢。

另外,這個時期平臺化已被提上日程,業(yè)務痛點決定了我們必須快速實施模塊整體的拆分和復用,而優(yōu)雅的設計模式并不適合解決這一類問題。

即使從復用性的角度來看,這樣的設計也會使得業(yè)務開發(fā)變得更為復雜、調(diào)試困難,對于新人來說難以勝任,最終推廣落地困難。

中間層實踐

通過多次實踐,我們認識到要實現(xiàn)兩端代碼復用,基礎(chǔ)庫的統(tǒng)一是必然的工作,是其他一切工作的基礎(chǔ)。否則必然導致復雜和難以維護的設計,最終導致兩端復用無法快速推進下去。

計算機界有一句名言:“計算機科學領(lǐng)域的任何問題都可以通過增加一個中間層來解決。”(原始版本出自計算機科學家 David Wheeler)我們當然有想過通過中間層設計屏蔽兩端的基礎(chǔ)庫差異。

例如網(wǎng)絡庫,外賣 App 基于 Volley 實現(xiàn),外賣頻道基于 Retrofit 實現(xiàn)。我們曾經(jīng)在 Volley 和 Retrofit 之上封裝了一層網(wǎng)絡框架,對外暴露統(tǒng)一的接口,上層可以切換底層依賴 Volley 或是 Retrofit。

但這個中間層并沒有上線,最終我們將兩端的網(wǎng)絡庫統(tǒng)一成了 Retrofit。

這里面有多個原因:

  • 首先 Retrofit 本身就是較高層次的封裝,并且擁有優(yōu)雅的設計模式,理論上我們很難封裝一套擴展性更強的接口。
  • 其次長期來看底層網(wǎng)絡框架變更的風險極低,并且適配網(wǎng)絡層的各種插件也是一件費時費力的事情,因此保持網(wǎng)絡中間層的性價比極低。
  • 此外將兩端的網(wǎng)絡請求都替換為中間層接口,顯然工作量遠大于只保留一端的依賴。

通過實踐我們認識到,中間層設計是一把雙刃劍。如果基礎(chǔ)框架本身的擴展性足夠強,中間層設計就顯得多此一舉,甚至喪失了原有框架的良好特性。

平臺化實踐

好的架構(gòu)源于不停地衍變,而非設計。對于外賣 Android 客戶端的平臺化架構(gòu)構(gòu)建也是經(jīng)歷了同樣的過程。

我們從考慮如何解決代碼復用的問題,逐漸的衍變成如何去解決代碼復用和平臺化的兩個問題。而實際上外賣平臺化正是解決兩端代碼復用的一劑良藥。

我們通過建立外賣平臺,將現(xiàn)有的外賣業(yè)務降級為一個頻道,將外賣業(yè)務以 aar 的形式分別接入到外賣平臺和美團平臺。這樣在解決外賣平臺化的同時,代碼復用的問題也將得到完美的解決。

平臺化架構(gòu)

經(jīng)過了整整一年的艱苦奮斗,形成了如圖所示的美團外賣 Android 客戶端平臺化架構(gòu),如下圖:

從底層到高層依次為平臺層、業(yè)務層和宿主層:

  • 平臺層,內(nèi)容包括承載上層的數(shù)據(jù)通信和頁面跳轉(zhuǎn);提供外賣核心服務,例如商品管理、訂單管理、購物車管理等;提供配置管理服務;提供統(tǒng)一的基礎(chǔ)設施能力,例如網(wǎng)絡、圖片、監(jiān)控、報警、定位、分享、熱修、埋點、Crash 上報等;提供其他管理能力,例如生命周期管理、組件化等。
  • 業(yè)務層,內(nèi)容包括外賣業(yè)務和垂直業(yè)務。
  • 宿主層,內(nèi)容包括 Waimai App 殼和美團外賣頻道 Waimai-channel 殼,這一層用于 Application 的初始化、dex 加載和其他各種必要的組件或基礎(chǔ)庫的初始化。

在構(gòu)建平臺化架構(gòu)的過程中,我們遇到這樣一個問題,如何長久的維持我們平臺化架構(gòu)的層級邊界。

試想,如果所有的代碼都在一個工程里面開發(fā),通過包名、約定去規(guī)范層級邊界,任何一個緊急的需求都可能破壞層級邊界。

維持層級邊界的最好辦法是什么?我們的經(jīng)驗是工程隔離。

平臺化的每一層都去做工程隔離,業(yè)務層的每個業(yè)務都建立自己的工程庫,實現(xiàn)工程隔離。同時,配套編譯腳本,檢查業(yè)務庫之間是否存在相互依賴關(guān)系。

工程隔離的好處是顯而易見的:

每個工程都可以獨立編譯、獨立打包。

每個工程內(nèi)部的修改,不會影響其他工程。

業(yè)務庫工程可以快速拆分出來,集成到其他 App 中。

但工程隔離帶來的另一個問題是,同層間的業(yè)務庫需要通信怎么辦?這時候就需要提供業(yè)務庫通信框架來解決這個問題。

業(yè)務庫通信框架

在拆分外賣商家業(yè)務庫的時候,我們就發(fā)這樣一個案例:在商家頁有一個業(yè)務,當發(fā)現(xiàn)當前商家是打烊的,就會彈出一個浮層,推薦相似的商家列表,而在我們之前劃分的外賣子業(yè)務庫里面,相似商家列表應該是屬于頁面庫里面的內(nèi)容。

那怎么讓商家業(yè)務庫訪問到頁面庫里面的代碼呢?如果我們將商家?guī)烊ヒ蕾図撁鎺欤俏覀兊膶蛹夁吔缇蜁淮蚱疲覀兊囊蕾囮P(guān)系也會變得復雜。

因此我們需要在架構(gòu)中提供同層間的通信框架,它去解決不打破層級邊界的情況下,完成同層間的通信。

匯總同層間通信的場景,大致上可以劃分為:

  • 頁面的跳轉(zhuǎn)。
  • 基本數(shù)據(jù)類型的傳遞(包括可序列化的共有類對象的傳遞)。
  • 模塊內(nèi)部自定義方法和類的調(diào)用。

針對上述情況,在我們的架構(gòu)里面提供了二種平級間的通信方式:scheme 路由和美團自建的 ServiceLoaders sdk。

scheme 路由本質(zhì)上是利用 Android 的 scheme 原理進行通信,ServiceLoader 本質(zhì)上是利用的 Java 反射機制進行通信。

scheme 路由的調(diào)用如圖所示:

最終效果:所有業(yè)務頁面的跳轉(zhuǎn),都需要通過平臺層的 scheme 路由去分發(fā)。通過 scheme 路由,所有業(yè)務都得到解耦,不再需要相互依賴而可以實現(xiàn)頁面的跳轉(zhuǎn)和基本數(shù)據(jù)類型的傳遞。

serviceloader 的調(diào)用如圖所示:

提供方和使用方通過平臺層的一個接口作為雙方交互的約束。使用方通過平臺層的 ServiceLoader 完成提供方的實現(xiàn)對象獲取。

這種方式可以解決模塊內(nèi)部自定義方法和類的調(diào)用,例如我們之前提到了商家?guī)煨枰{(diào)用頁面庫代碼的問題就可以通過 ServiceLoader 解決。

外賣內(nèi)核模塊設計

在實踐的過程中,我們也遇到業(yè)務本身就不好劃分層級邊界的業(yè)務。大家可以從美團外賣三層架構(gòu)圖上,看出外賣業(yè)務庫,像商家、訂單等,是和外賣的垂類業(yè)務庫是同級的。

而實際上外賣業(yè)務的子業(yè)務是否應該和垂類業(yè)務保持同層是一個目前無法確定的事情。

目前,外賣接入的垂類業(yè)務商超業(yè)務,是隸屬于外賣業(yè)務的子頻道,它依然依賴著外賣的核心 Model、核心服務,包括商品管理、訂單管理、購物車管理等。

因此目前它和外賣業(yè)務的商家、訂單這樣的子業(yè)務庫同層是沒有問題的。但隨著商超業(yè)務的發(fā)展,商超業(yè)務未來可能會建設自己的商品管理、訂單管理、購物車管理的服務,那么到時商超業(yè)務就會上升到和外賣業(yè)務一樣同層的業(yè)務。

這時候,外賣核心管理服務,處在平臺層,就會導致架構(gòu)的層級邊界變得不再清晰。

我們的解決辦法是通過設計一個屬于外賣業(yè)務的內(nèi)核模塊來適應未來的變化,內(nèi)核模塊的設計如圖:

  • 內(nèi)圈為基礎(chǔ)模型類,這些模型類構(gòu)成了外賣核心業(yè)務(從門店→點菜→購物車→訂單)的基礎(chǔ)。
  • 中間圈為依賴基礎(chǔ)模型類構(gòu)建的基礎(chǔ)服務(CRUD)。
  • 最外圈為外賣的各維度業(yè)務,向內(nèi)依賴基礎(chǔ)模型圈和外賣基礎(chǔ)服務圈。

如果未來確定外賣平臺需要接入更多和外賣平級的業(yè)務,且最內(nèi)圈都完全不一樣,我們將把外賣內(nèi)核模塊上移,在外賣業(yè)務子庫下建立對內(nèi)核模塊的依賴。

如果未來只是有更多的外賣子業(yè)務的接入,那就繼續(xù)保留我們現(xiàn)在的架構(gòu);如果未來接入的業(yè)務基礎(chǔ)模型類一樣,但自己的業(yè)務服務需要分化,那么我們將保留內(nèi)核模塊最核心的內(nèi)圈,并抽象出服務層由外賣和商超上層自己實現(xiàn)真正的服務。

業(yè)務庫拆分

在拆分業(yè)務庫的時候,我們面臨著這樣的問題:業(yè)務之間的關(guān)系是較為復雜的,如何去拆分業(yè)務庫,才是較為合理的呢?

一開始我們準備根據(jù)外賣業(yè)務核心流程:頁面→商家→下單,去拆分外賣業(yè)務。

但是隨著外賣子頻道業(yè)務的快速發(fā)展,子頻道業(yè)務也建立了自己的研發(fā)團隊,在頁面、商家、下單等環(huán)節(jié),也開始建立自己的頁面。

如果我們?nèi)匀话凑胀赓u下單的流程去拆分庫,那在同一個庫之間,就會有外賣團隊和外賣子頻道團隊共同開發(fā)的情況,這樣職責邊界很不清晰,在實際的開發(fā)過程中,肯定會出現(xiàn)理不清的情況。

我們都知道軟件工程領(lǐng)域有所謂的康威定律:

Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations. 

- Melvin Conway(1967)

翻譯成中文的大概意思是:設計系統(tǒng)的組織,其產(chǎn)生的設計等同于組織之內(nèi)、組織之間的溝通結(jié)構(gòu)。

在康威定理的指導下:我們認為技術(shù)架構(gòu)應該反映出團隊的組織結(jié)構(gòu),同時,組織結(jié)構(gòu)的變遷,也應該導致技術(shù)架構(gòu)的演進。

美團外賣平臺下包含外賣業(yè)務和垂直品類業(yè)務,對于在我們團隊中已經(jīng)有了組織結(jié)構(gòu),優(yōu)先組織結(jié)構(gòu),去拆出獨立的業(yè)務庫,方便子業(yè)務庫的同學內(nèi)部溝通協(xié)作,減少他們跨組織溝通的成本。

同時,我們將負責外賣業(yè)務的大團隊,再進一步細化成頁面小組、商家小組和訂單小組,由這些小組的同學去在外賣業(yè)務下完成更細維度的外賣子業(yè)務庫拆分。

根據(jù)組織結(jié)構(gòu)劃分的業(yè)務庫,天然的存在業(yè)務邊界,每個同學都會按照自己業(yè)務的目標去繼續(xù)完善自己的業(yè)務庫。這樣的拆庫對內(nèi)是高內(nèi)聚,對外是低耦合的,有效的降低了內(nèi)外溝通協(xié)作的成本。

工程內(nèi)代碼隔離

在實現(xiàn)工程隔離之后,我們發(fā)現(xiàn)工程內(nèi)部的代碼還是可以相互引用的。工程內(nèi)部如果也不能實現(xiàn)代碼的隔離,那么工程內(nèi)部的邊界就是模糊的。

我們希望工程內(nèi)至少能夠?qū)崿F(xiàn)頁面級別的代碼隔離,因為 Activity 是組成一個 App 的頁面單元,圍繞這個 Activity,通常會有大量的代碼及資源文件,我們希望這些代碼和資源文件是被集中管理的。

通常我們想到的做法是以 module 工程為單位的相互隔離,但在 module 是相對比較重的一個約束,難道每個 Activity 都要建一個 module 嗎?

這樣代碼結(jié)構(gòu)會變得很復雜,而且針對一些大的業(yè)務體,又會形成巨大化的 module。

那我們又想到規(guī)范代碼,用包名去人為約定,但靠包名約束的代碼,邊界模糊,時不時的緊急需求,就把包名約定打破了,而且資源文件的擺放也是任意的,遷移成本高。

那怎么去解決工程內(nèi)部的邊界問題呢?《微信的模塊化架構(gòu)重構(gòu)實踐》一文中提到了一個重要的概念 p(pins)工程,p 工程可謂是工程內(nèi)約束代碼邊界的重要法寶。

通過在 Gradle 里面配置 sourceSets,就可以改變工程內(nèi)的代碼結(jié)構(gòu)目錄,完成代碼的隔離,配置示例:

效果如圖所示:

從上圖可以看出,這個業(yè)務庫被以頁面為單元拆分成了多個 p 工程,每個 p 工程的邊界都是清楚的,實現(xiàn)了工程內(nèi)的代碼隔離。

工程內(nèi)代碼隔離帶來的好處顯而易見:

  • p 工程實現(xiàn)了最小粒度的代碼邊界約束。
  • 工程內(nèi)模塊職責清晰。
  • 業(yè)務模塊可以被快速的拆分出來。

代碼復用

p 工程滿足了工程內(nèi)代碼隔離的需求,但是別忘了,我們每個模塊在外賣兩個終端上(外賣 App&美團 App)上可能存在差異,如果能在模塊內(nèi)部實現(xiàn)兩端差異,我們的目標才算達成。

基于上述考慮,我們想到了使用 Gradle 提供的 productFlavors 來實現(xiàn)兩端的差異化。

為此,我們需要定義兩個 flavor:wm 和 mt。

但是,這樣生成的 p 工程是并列的,也就是說,各個 p 工程中所有的差異化代碼都需要被存放在這兩個 flavor 對應的 SourceSet 下,這豈不是跟模塊間代碼隔離的理念相違背?

理想的結(jié)構(gòu)是在 p 工程內(nèi)部進行 flavor 劃分,由 p 工程內(nèi)部包容差異化,繼續(xù)改成 Gradle 腳本如下:

最終工程結(jié)構(gòu)變成如下:

通過 p 工程和 flavor 的靈活應用,我們最終將業(yè)務庫配置成以 p 工程為維度的模塊單元,并在 p 工程內(nèi)部兼容兩端的共性及差異,代碼復用被很好的解決了。

同時,兩端差異的問題是歸屬在 p 工程內(nèi)部自己處理的,并沒有建立中間層,或?qū)⒉町悞伣o上層殼工程去完成,這樣的設計遵守了邊界清晰,向下依賴的原則。

但是,工程內(nèi)隔離也存在與工程隔離一樣的問題:同層級 p 工程需要通信怎么辦?

我們在拆分商家?guī)斓臅r候,就面臨這這樣的問題,商品活動頁和商品詳情頁,可以根據(jù)頁面維度,去拆分成 2 個 p 工程,這兩個頁面都會用到同一個商品樣式的 item。

如何讓同層間商品活動頁 p 工程和商品詳情頁 p 工程訪問到商品樣式 item呢?

在實際拆庫的實踐中,我們逐漸的探索出三級工程結(jié)構(gòu)。三級工程結(jié)構(gòu)不僅可以解決工程內(nèi)p工程通信的問題,而且可以保持架構(gòu)的靈活性。

三級工程結(jié)構(gòu)

三級工程結(jié)構(gòu),指的是工程→module→p 工程的三級結(jié)構(gòu)。

我們可以將任何一個非常復雜的業(yè)務工程內(nèi)部劃分成若干個獨立單元的 module 工程,同時獨立單元的 module 工程,我們可以繼續(xù)去劃分它內(nèi)部的獨立 p 工程。

因為 module 是具備編譯時的代碼隔離的,邊界是不容易被打破的,它可以隨時升級為一個工程。

需要通信的 p 工程依賴 module 的主目錄,base 目錄,通過base目錄實現(xiàn)通信。

工程和 module 具有編譯上隔離代碼的能力,p 工程具有最小約束代碼邊界的能力,這樣的設計可以使得工程內(nèi)邊界清晰,向下依賴。

設計如圖所示:

三級工程結(jié)構(gòu)的最大好處就是,每級都可按照需要靈活的升級或降級,這樣靈活的升降級,可以隨時適應團隊組織結(jié)構(gòu)的變化,保持架構(gòu)拆分合并的靈活性,從而動態(tài)的滿足了康威定理。

工程化建設

平臺化一個直觀的結(jié)果就是產(chǎn)生了很多子庫,如何對這些子庫進行有效的工程化管理將是一個影響團隊研發(fā)效率的問題。目前為止,我們從以下兩個方面做了改進。

一鍵切源碼

主工程集成業(yè)務庫時,有兩種依賴模式:aar 依賴和源碼依賴。默認是 aar 依賴,但是在平時開發(fā)時,經(jīng)常需要從 aar 依賴切換到源碼依賴,比如新需求開發(fā)、bugfix 及排查問題等。

正常情況我們需要在各個工程的 build. 中將 compile aar 手動改為 compile project,如果業(yè)務庫也需要依賴平臺庫源碼,也要做類似的操作。

如下圖所示:

這樣手動操作會帶來兩個問題:

  • build.gradle 改動頻繁,如果開發(fā)人員不小心 push 上去了,將會造成各種沖突。
  • 當業(yè)務庫越來越多時,這種改動的成本就越來越大了。

鑒于這種需求具備通用性,我們開發(fā)了一個 Gradle 插件,通過主工程的一個配置文件(被 git ignore),可一鍵切換至源碼依賴。

例如需要源碼依賴商家?guī)欤敲粗恍枰谥鞴こ讨袑⒃搸斓脑创a依賴開關(guān)打開即可。商家?guī)爝€依賴平臺庫,默認也是 aar 依賴,如果想改成源碼依賴,也只需把開關(guān)打開即可。

一鍵打包

業(yè)務庫增多以后,構(gòu)建流程也變得復雜起來,我們交付的產(chǎn)物有兩種:外賣 App 的 apk 和外賣頻道的 aar。

外賣 App 的情況會簡單一些,在 Jenkins 上關(guān)聯(lián)各個業(yè)務庫指定分支的源碼,直接打包即可。

而外賣頻道的情況則比較復雜,因為受到美團平臺的一些限制,頻道打包不能直接關(guān)聯(lián)各個業(yè)務庫的源碼,只能依賴 aar。

按照傳統(tǒng)做法,需要逐個打業(yè)務庫的 aar,然后統(tǒng)一在頻道工程中集成,最后再打頻道 aar,這樣效率實在太低。為此,我們改進了頻道的打包流程。

如下圖所示:

先打平臺庫 aar,打完后自動提 PR 到各個業(yè)務庫去修改平臺庫的版本號,接著再逐個觸發(fā)業(yè)務庫去打 aar。

業(yè)務庫打完 aar 之后再自動提 PR 到頻道主庫去修改業(yè)務庫的版本號,等全部業(yè)務庫 aar 打完后最后再自動觸發(fā)打頻道主庫的 aar,至此一鍵打包完畢。

平臺化總結(jié)

從搜索庫拆分的第一次嘗試算起,外賣 Android 客戶端在架構(gòu)上的持續(xù)探索和實踐已經(jīng)經(jīng)歷了 2 年多的時間。

起初為了解決兩端代碼復用的問題,我們嘗試過自上而下的強行拆分和復用,但很快就暴露出層次混亂、邊界模糊帶來的問題,并且認識到如果不能提供兩端差異化的解決方案,代碼復用是很難持續(xù)的。

后來我們又嘗試過運用設計模式約束邊界,先實現(xiàn)解耦再進行復用,但在推廣落地過程中認識到復雜的設計很難快速推進下去。

在平臺化開始的時候,團隊已經(jīng)形成了設計簡單、邊界清晰的架構(gòu)理念。我們將整體結(jié)構(gòu)劃分為宿主層、業(yè)務層、平臺層,并嚴格約束層次間的依賴關(guān)系。

在業(yè)務模塊拆分的過程中,我們借鑒微信的工程結(jié)構(gòu)方案,按照三級工程結(jié)構(gòu)劃分業(yè)務邊界,實現(xiàn)靈活的代碼隔離,并降低了后續(xù)模塊遷出和遷入成本,使得架構(gòu)動態(tài)滿足康威定律。

在兩端代碼復用的問題上,我們認識到要實現(xiàn)可持續(xù)的代碼復用,必須自下向上的逐步統(tǒng)一兩端底層的基礎(chǔ)依賴,同時又能容易的支持兩端上層業(yè)務的差異化處理。

使用 Flavor 管理兩端的差異代碼,盡量減少向上依賴,在具體實施時應用之前積累的解耦設計的經(jīng)驗,從而滿足了架構(gòu)的可伸縮性。

沒有一個方案能獲得每個人的贊同。在平臺化的實施過程中,團隊成員多次對方案選型發(fā)生過針鋒相對的討論。

這時我們會拋開技術(shù)方案,回到問題本身,去重新審視業(yè)務的痛點,列出要解決的問題,再回過頭來看哪一個方案能夠解決問題。

雖然我們并不常常這么做,但某些時刻也會強制決策和實施,遇到問題再復盤和調(diào)整。

任何一種設計理念都有其適用場景。我們在不斷關(guān)注業(yè)內(nèi)一些優(yōu)秀的架構(gòu)和設計理念,以及公司內(nèi)部美團 App、點評 App 團隊的平臺化實踐經(jīng)驗,學習和借鑒了許多優(yōu)秀的設計思想,但也由于盲目濫用踩過不少坑。

我們認識到架構(gòu)的選擇正如其他技術(shù)問題一樣,應該是面向問題的,而不是面向技術(shù)本身。

架構(gòu)的演進必須在理論和實踐中交替前行,脫離了其中一個談論架構(gòu),都將是個悲劇。

展望

平臺化之后,各業(yè)務團隊的協(xié)作關(guān)系和開發(fā)流程都發(fā)生了很大轉(zhuǎn)變。在如何提升平臺支持能力,如何保持架構(gòu)的穩(wěn)定性,如何使得各業(yè)務進一步解耦等問題上,我們又將面對新的問題和挑戰(zhàn)。

其中有三個問題是亟待我們解決的:

  • 要確保在長期的業(yè)務迭代中架構(gòu)不被破壞,除了流程規(guī)范之外,還需要在本地編譯、遠程提交、代碼合并、打包提測等各個階段建立更健全的檢查工具來約束,而目前這些工具鏈還不完善。
  • 插件化架構(gòu)是平臺型 App 集成的最好方式,不僅使得子業(yè)務具備動態(tài)發(fā)布的能力,還可以解決令人頭疼的編譯速度問題。目前美團平臺已經(jīng)在部分業(yè)務上較好的實現(xiàn)了插件化集成,外賣正在跟進。
  • 統(tǒng)一頁面級開發(fā)的標準化框架,可以解決代碼的可維護性、可測試性,和更細粒度的可復用性,并且有利于各種自動化方案的實施。目前我們正在部分業(yè)務嘗試,后續(xù)會持續(xù)推進。

參考資料:

  • MVP + Clean Architecture
  • 58同城沈劍:好的架構(gòu)源于不停地衍變,而非設計
  • 每個架構(gòu)師都應該研究下康威定理
  • 微服務架構(gòu)的理論基礎(chǔ) - 康威定律
  • 架構(gòu)的本質(zhì)是管理復雜性,微服務本身也是架構(gòu)演化的結(jié)果
  • 微信 Android 模塊化架構(gòu)重構(gòu)實踐
  • 配置構(gòu)建變體
  • 美團 App 插件化實踐

作者:吳凱、曉飛、海冰

吳凱,美團點評技術(shù)專家。2016 年加入美團點評,目前負責外賣用戶端 Android 團隊,主要致力于外賣 Android 平臺化業(yè)務支持和技術(shù)建設。

曉飛,美團點評資深工程師。2015 年加入原美團,是外賣 Android 的早期開發(fā)者之一,目前作為外賣 Android App 負責人,主要負責平臺和業(yè)務架構(gòu)。

海冰,美團點評高級工程師。2015 年加入原美團,曾支持開店寶等 B 端業(yè)務,目前作為外賣 Android App 主力開發(fā),負責商家容器模塊,及平臺化相關(guān)推進工作。

責任編輯:武曉燕 來源: 美團點評技術(shù)團隊
相關(guān)推薦

2016-11-27 20:43:26

云計算迭代

2022-04-29 09:10:00

算法人工智能技術(shù)

2022-08-09 09:18:47

優(yōu)化實踐

2022-03-25 10:47:59

架構(gòu)實踐美團

2018-07-13 09:53:27

移動應用美團代碼

2022-08-25 22:24:19

架構(gòu)電商系統(tǒng)

2022-08-26 20:00:00

系統(tǒng)架構(gòu)

2018-12-07 12:54:22

App美團外賣iOS客戶端

2017-12-08 18:45:41

程序員外賣運維

2017-06-01 10:52:35

互聯(lián)網(wǎng)

2018-01-19 14:04:05

人工智能機器學習智能語音

2017-07-03 15:32:49

數(shù)據(jù)庫MySQL架構(gòu)

2017-12-29 08:54:58

高可用數(shù)據(jù)庫架構(gòu)

2019-08-23 13:10:39

美團點評Kubernetes集群管理

2022-08-09 08:42:15

引擎方案

2023-11-14 20:51:08

2017-03-22 17:51:43

互聯(lián)網(wǎng)

2018-06-01 10:08:00

DBA美團SQL

2015-11-16 16:00:21

點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 久精品久久 | 一二三四在线视频观看社区 | 日韩欧美国产电影 | 国产一级片在线播放 | 色天天综合| 日屁视频| 久热久热 | 啪啪免费 | 国产日日操 | 伊人久久在线 | 午夜影院 | 国产日韩欧美一区二区 | 欧美日韩久久 | 国产91观看 | 青青草一区 | 热久久性| 欧美韩一区二区三区 | 91在线电影 | 成人在线观看亚洲 | 中文字幕av一区二区三区 | 男女羞羞视频在线免费观看 | 亚洲精彩视频在线观看 | 久久99精品久久久久 | 国产精品久久视频 | 欧美日韩在线观看一区 | 亚洲小视频在线播放 | 亚洲区视频 | 久久综合一区二区 | 久久久精品综合 | 中文在线播放 | 涩爱av一区二区三区 | 日韩中文字幕免费在线观看 | 久久久精品一区二区三区四季av | 欧美xxxⅹ性欧美大片 | 又黄又色| 欧美 日韩 国产 在线 | 成人小视频在线观看 | 亚洲国产精品久久久久 | 欧美中文字幕一区二区三区 | 91免费在线视频 | 欧美在线资源 |