零.背景
服務端需要支持多種前端設備下的用戶體驗時,常常面臨現有 API 與某一端 UI 緊耦合的情況。
比如為 PC 端頁面設計的 API 需要支持移動端,發現現有接口從設計到實現都與桌面 UI 展示需求強相關,無法簡單適應移動端的展示需求。
一.現狀
一個後端 API 層對多個前端(PC ��、移動端等)的話,一般是支持一端後,再添加更多功能來支持其它端。
It's tempting to design a single back-end API to support all clients with a reusable API.
多個前端 UI 要求的 API 相似的話,這樣做沒什麼問題(稍微增強下現有 API 就能支持另一種前端體驗),但移動端 UI 體驗通常與 PC 端不一樣。
Valuable services support many variations in clients, such as mobile versus web and different forms of web interface.
例如:
-
屏幕空間小,所能顯示的數據也少
-
建立多個連接會增加耗電,前端做數據聚合成本高
-
交互方式差異很大,後端 API 需要支持的功能也不同。比如 PC 端填表單,移動端掃碼……
所以,移動端通常要:
-
進行不同的(或更少的)API 調用,比如做聚合
-
展示不同的(或更少量的)數據
另外,一個後端 API 通常要為多個前端應用提供支持,此時單一後端可能會成為版本迭代的瓶頸,因為每版工作量巨大(對接 N 個前端應用)。從而產生一個龐大的獨立後端團隊,負責給多個前端團隊提供 API,然後各前端團隊都需要與該團隊溝通變更,而該團隊要平衡多個前端團隊的需求優先級……繼而面臨跨團隊協作低效、資源協調困難等問題。
二.BFF 的由來
由於以上種種,我們不再寄希望於一個大後端為多端體驗統一提供 API 支持,而是給每種用戶體驗對應一個後端(one backend per user experience),稱為Backend For Frontend (BFF),譯作用戶體驗適配層。
Consequently it's often best to define different back-end services for each kind of front-end client.
概念上,把每個前端應用拆成兩部分:客戶端應用與服務端的部分(BFF)。其中,BFF 是面向特定用戶體驗的,由實現這部分 UI 的前端團隊負責實現及維護(即 UI 與對應的 BFF 由同一個團隊負責)。
These back ends should be developed by teams aligned with each front end to ensure that each back end properly meets the needs of its client.
同一個團隊的優勢在於:
-
更容易根據 UI 來定義或調整 API
-
簡化了客戶端、服務端的發布流程(依賴項更少了)
-
一個 BFF 只專注於一個 UI,更小也更靈活
從服務的角度看,BFF 實際上是限制了單一服務所支持的消費者(指前端應用)數量,從而讓它們更易於使用(更貼合前端需要)和更改,並幫助開發前端應用的團隊保留更多的自主權:
The simple act of limiting the number of consumers they support makes them much easier to work with and change, and helps teams developing customer-facing applications retain more autonomy.
三.具體實現
要求 BFF 與用戶體驗一對一,也就是要把一個大後端拆成多個小後端。
細分粒度
既然要拆分,那應該按什麼來分?細分到什麼程度?
不難想到 3 種拆分方式:
-
用戶體驗級(UI 級):每一種 UI 交互對應一個 BFF,比如 PC 端 1 個,移動端 3 個(例如小屏手機、中屏手機和大屏手機三者 UI 交互差異很大的話,有必要拆成 3 個 BFF)
-
端級:每種前端設備對應一個 BFF,比如 PC、Android、iOS、手錶、車機等等
-
團隊級:每個前端團隊對應一個 BFF,按現有組織結構來分
建議進行用戶體驗級拆分,因為端級拆分如果多端 UI 類似的話,接口大概率是能夠直接復用的,沒必要拆,而組織結構是能夠靈活調整的,不應該限制技術方案。
對接下遊服務(微服務)
每個 BFF 需對接多個下遊服務,那麼勢必存在幾個問題:
-
如何對接多個技術棧不同的下遊服務?
-
如何管理、如何組合這些調用?
-
某一個調用失敗時,如何保障可用性?
統一的 RPC 協議能夠抹平下遊服務技術棧的差異;對於異步調用的管理,可以藉助 RxJava、Finagle 等事件機制來簡化這些異步流程控制;部分調用失敗時的可用性問題,則可以通過在 BFF 層容錯,同時前端保證可接受不完整的響應內容來解決。
復用問題
拆開之後,多個 BFF 之間容易產生冗餘代碼,尤其是一些通用的後端邏輯(如授權、認證、限流等等)。
為了消除 BFF 間的代碼冗餘,一般採用兩種做法:
-
要麼多 BFF 合一
-
要麼在 BFF 之上加一層 Edge API service
多 B 合一的話,又回到了最初的問題,因為想要靈活性才拆開,又因為想要復用而合起來……白折騰了。另一個選項是加一層網關服務(Edge API service),把通用邏輯放進去,讓 BFF 得以專注業務邏輯:
It validates and authenticates incoming requests; it enforces rate limits to protect the platform from undue load, and it routes requests to the appropriate upstream services. Factoring these high-level features into the edge service is a huge win for platform simplicity.
回到復用問題本身,我們想消除冗餘,又不想因為抽離可復用代碼而導致 BFF 間緊耦合,所以就有了一種折衷的態度:容忍 BFF 間冗餘、消除單 BFF 內冗餘。也就是允許一定程度的 BFF 間冗餘。
當然,復用的前提是多 BFF 技術棧相同,然後識別出冗餘部分,並重構掉。具體地,到需要抽離公用部分的階段,有幾個選擇:
-
提出公共庫
-
抽出去作為獨立 service
-
下沉到下遊服務
但公共庫通常是耦合的主要來源,比如調用下遊服務的公共邏輯會引發 BFF 間耦合。獨立 service 的方案相對更好一些,可以進一步把新 service 概念化到領域模型裡。類似地,也可以把公用邏輯下沉到下遊服務,讓平級的下遊服務變成有依賴的樹結構。
無論怎樣解決復用問題,都應該在有必要進行復用時才去做,一般原則是:
Creating an abstraction when you're about to implement something for the 3rd time.
四.應用場景
與移動設備相比,PC 設備性能足夠好,那麼是不是能直接調多個下遊服務(成本不很高),不走 BFF 呢?
實際上,與直接面向前端應用的下遊服務相比,BFF 的意義在於適合用來實現:
-
服務端模板
-
對數據進行聚合(合併多個接口調用)、編排(格式化成前端想要的樣子)、裁剪(去掉前端不需要的信息)
-
緩存聚合調用的結果
-
給某種 UI 體驗(如移動端)提供特定功能
-
供第三方使用的 API,便於維護因第三方限制而添加的那部分邏輯
因為 BFF 是位於下遊服務之上的一層,並且細分到用戶體驗粒度,所以要比下遊服務更靈活,尤其適合為第三方提供定製 API 等差異化場景。
五.業界實踐
按照 BFF 理念,把大後端按前端體驗拆分開,如下圖:

具體實踐中,BFF 通常不是圖示的樣子,主要變化在於:
-
按業務線拆分 BFF
-
加一層網關,負責實現路由、認證、監控、限流熔斷、安全等功能
按業務線拆分的 BFF 更像是建立在下遊基礎服務之上的業務型微服務,只是���些微服務由對應業務的前端團隊負責開發維護。廣義的,可以理解為更細粒度的 BFF,即每塊業務對應一個 BFF(不再按用戶體驗差異去分)。
網關層負責實現通用的邊界服務,如認證、限流等,讓 BFF 更專注於業務相關的部分:
前端體驗
--------------------
^ ^
| |
網關
------------
BFF BFF
----- -----
^ ^ ^ ^
/ \ / \
--------------------
下遊服務
更有甚者,把網關層也拆開與 BFF 一一對應:
前端體驗
--------------------
^ ^
| |
網關 網關
----- -----
BFF BFF
----- -----
^ ^ ^ ^
/ \ / \
--------------------
下遊服務
P.S.另外,還有不引入 BFF,而只添一層轉發服務來解決數據的聚合、編排、裁剪等問題的,類似於 GraphQL,但容易出現無意義的數據透傳,所以一般不是全覆蓋的(並非所有數據請求都走轉發服務):
前端體驗
--------------------
^ ^
| |
轉發服務 |
-------- |
^ ^ |
/ \ |
--------------------
下遊服務
探索
實踐中對 BFF 的探索方向主要有 3 個:
-
穩定性:保障 BFF 的可靠性,如通過日誌、錯誤分析、監控、報警等手段
-
同構:讓 BFF 與前端體驗使用相同的技術棧,如基於 Node 的同構方案
-
一體化:一方面針對下遊服務提供 mock 方案,另一方面允許同構、非同構應用共存
畢竟在 BFF 模式下,要求前端開發掌握一定程度的全棧知識(如服務端技能,運維、安全等知識),所以,自然地想要通過同構或一體化方案來提升開發體驗,降低門檻,讓技術無感化。
希望越來越多的開發者,可以不用再關注流程、構建、環境、部署等各種事,希望能做到技術無感化(Techless),讓每一位開發著能安安靜靜的快樂編碼。
六.優勢
關注點分離
BFF 模式最大好處是關注點分離(separation of concerns),下遊服務可以專注於業務領域模型,前端 UI 專注於用戶體驗:
後端可以專注於業務領域,更多從領域模型的視角去思考問題,頁面視角的數據則交給前端型全棧工程師去搞定。領域模型與頁面數據是兩種思維模式,通過 BFF 可以很好地解耦開,讓彼此更專業高效。
從分工的角度看:
BFF 模式不僅僅是一種技術架構,從社會分工角度講,BFF 更是一種多元價值導向的分層架構,每一層都有不錯的空間去施展。
自主權
從團隊角度看,傳統的前後端分離主要問題在於:
-
服務的擴展性及複雜度
-
前後端團隊的多對一關係,因為前端團隊必然需要細分(由於技術實現的差異,需要專人來做)
-
跨團隊協作成本,例如前端 UI 開發過程中,後端 API 可能發生變化(存在跨團隊的溝通協調成本)
在 BFF 模式下,API 的 owner 是負責實現對應用戶體驗的前端團隊,也就是說前端團隊擁有 API 的自主權,可以快速調整變化。
前端與 BFF 團隊一體化的另一個好處是,可以靈活選擇由客戶端實現還是服務實現(比如通用性強的由服務實現,甚至為了快速發版過審也可以由服務實現),而不需要跨團隊協調。
暫無評論,快來發表你的看法吧