模式:前端的後端

模式:前端的後端

寫於

面向 UI 和外部各方的單一用途邊緣服務

介紹

隨着 Web 的出現和成功,交付用戶界面的實際方式已從胖客戶端應用程序轉向通過 Web 交付的界面,這一趨勢也促進了基於 SAAS 的解決方案的總體增長。通過網絡提供用戶界面的好處是巨大的 - 主要是因爲(在大多數情況下)完全消除了客戶端安裝的成本,從而顯着降低了發佈新功能的成本。

然而,這個更簡單的世界並沒有持續多久,因爲移動時代很快就到來了。現在我們遇到了問題。我們擁有服務器端功能,我們希望通過桌面 Web UI 和一個或多個移動 UI 來公開這些功能。對於最初開發時考慮到桌面 Web UI 的系統,我們經常面臨適應這些新型用戶界面的問題,通常是因爲我們已經在桌面 Web UI 和我們支持的服務之間建立了緊密的耦合。

通用 API 後端

適應多種類型 UI 的第一步通常是提供單一的服務器端 API,並隨着時間的推移根據需要添加更多功能以支持新型移動交互:

通用 API 後端

如果這些不同的 UI 想要進行相同或非常相似的調用,那麼這種通用 API 很容易成功。然而,移動體驗的本質通常與桌面 Web 體驗截然不同。首先,移動設備的可供性非常不同。我們的屏幕空間較少,這意味着我們可以顯示的數據較少。打開與服務器端資源的大量連接會耗盡電池壽命和有限的數據計劃。其次,我們想要在移動設備上提供的交互的性質可能會有很大不同。想想一個典型的實體零售商。在桌面應用程序上,我可能允許您查看待售商品、在線訂購或在商店預訂。在移動設備上,我可能希望允許您掃描條形碼以進行價格比較或在商店中爲您提供基於上下文的優惠。隨着我們構建了越來越多的移動應用程序,我們逐漸意識到人們對它們的使用方式非常不同,因此我們需要公開的功能也會有所不同。

因此,在實踐中,我們的移動設備將希望進行不同的調用、更少的調用,並且希望顯示與桌面設備不同(並且可能更少)的數據。這意味着我們需要向 API 後端添加額外的功能來支持我們的移動界面。

通用 API 後端的另一個問題是,它們根據定義爲多個面向用戶的應用程序提供功能。這意味着單一 API 後端可能會成爲推出新交付時的瓶頸,因爲試圖對同一個可部署工件進行大量更改。

通用 API 後端傾向於承擔多種職責,因此需要大量工作,通常會導致專門創建一個團隊來處理此代碼庫。這可能會使問題變得更糟,因爲現在前端團隊必須與一個單獨的團隊進行交互才能做出更改 - 該團隊必須平衡不同客戶團隊的優先級,並且還必須與多個下游團隊合作以完成更改。當新的 API 可用時使用它們。可以說,此時我們剛剛在我們的架構中創建了一個智能中間件,它並不專注於任何特定的業務領域 - 這與許多人對明智的面向服務架構應該是什麼樣子的看法背道而馳。

使用通用支持的 API 時的常見團隊結構

介紹前端的後端

我在 REA 和 SoundCloud 中看到的解決這個問題的一種方法是,不是使用通用 API 後端,而是爲每個用戶體驗提供一個後端 - 或者(前 SoundClouder)Phil Calçado將其稱爲後端對於前端 (BFF)。從概念上講,您應該將面向用戶的應用程序視爲兩個組件 - 位於您的邊界之外的客戶端應用程序和位於您的邊界內的服務器端組件(BFF)。

BFF 與特定的用戶體驗緊密耦合,並且通常由與用戶界面相同的團隊維護,從而更容易根據 UI 的需要定義和調整 API,同時還簡化了兩者的發佈過程客戶端和服務器組件。

每個用戶界面使用一個服務器端 BFF

BFF 緊密關注單個 UI,而且只是那個 UI。這使得它能夠集中,因此會更小。

有多少最好的朋友?

當談到在不同平臺上提供相同(或相似)的用戶體驗時,我看到了兩種不同的方法。我更喜歡的模型是爲每種不同類型的客戶嚴格指定一個 BFF - 這是我在 REA 看到使用的模型:

不同的移動平臺,不同的 BFF,如 REA 使用的那樣

我在 SoundCloud 看到過使用的另一種模型,每種類型的用戶界面使用一個 BFF。因此,Android 和 iOS 版本的偵聽器本機應用程序都使用相同的 BFF:

爲不同的移動後端配備一個 BFF,如 SoundCloud 所使用的那樣

我對第二種模型的主要擔憂是,使用單個 BFF 的客戶類型越多,它因處理多個問題而變得臃腫的誘惑就越大。這裏要理解的關鍵是,即使共享 BFF,它也是針對同一類用戶界面的 - 因此,雖然 iOS 和 Android 的 SoundCloud 偵聽器本機應用程序使用相同的 BFF,但其他本機應用程序將使用不同的 BFF(例如例如,新的 Creator 應用程序 Pulse 使用不同的 BFF)。如果同一個團隊同時擁有 Android 和 iOS 應用程序並且也擁有 BFF,我也會更輕鬆地使用此模型 - 如果這些應用程序由不同的團隊維護,我更傾向於推薦更嚴格的模型。因此,您可以將組織結構視爲模型最有意義的主要驅動因素之一(康威定律再次獲勝)。值得注意的是,我採訪過的 SoundCloud 工程師表示,如果今天再次做出決定,他們可能會重新考慮爲 Android 和 iOS 偵聽器應用程序配備一個 BFF。

我非常喜歡 Stewart Gleadow(他又將 Phil Calçado 和 Mustafa Sezgin 歸功於他)的一條指導方針是“一次體驗,一次最好的朋友”。因此,如果 iOS 和 Android 的體驗非常相似,那麼就更容易證明擁有一個 BFF 是合理的。然而,如果他們的分歧很大,那麼擁有單獨的好朋友就更有意義了。

Pete Hodgson 觀察到,最好的朋友在圍繞團隊邊界對齊時效果最佳,因此團隊結構應該決定您擁有多少最好的朋友。因此,如果您有一個移動團隊,您應該有一個最好的朋友,但如果您有單獨的 iOS 和 Android 團隊,您就會有單獨的最好的朋友。我擔心的是,團隊結構往往比我們的系統設計更加流動。因此,如果您有一個專門負責移動設備的 BFF,然後將團隊分爲 iOS 和 Android 專業領域,那麼您是否也必須拆分 BFF?如果最好的朋友已經分開,那麼拆分團隊會更容易,因爲您可以重新分配已經獨立的資產的所有權。不過,最好的朋友和團隊結構之間的相互作用很重要,我們將很快對此進行探討。

通常,減少 BFF 數量的驅動因素是重用服務器端功能,以避免過多的重複,但還有其他方法可以處理這個問題,我們很快就會介紹。

和多個下游服務(微服務!)

對於後端服務數量較少的架構來說,BFF 可能是一種有用的模式。然而,對於使用大量服務的組織來說,它們可能是必不可少的,因爲聚合多個下游調用以提供用戶功能的需求急劇增加。在這種情況下,對 BFF 的單次調用通常會導致對微服務的多個下游調用。例如,想象一個電子商務公司的應用程序。我們想要拉回用戶願望清單中的商品列表,顯示庫存水平和價格:

剎車 - 獻血 有存貨!(剩餘 14 項) 5.99 美元 現在下單
藍色果汁 - 可追溯 缺貨 17.50 美元 預購
熱門芯片 - 爲什麼有意義? 速度快(還剩 2 件) 9.99 美元 現在下單

多個服務保存着我們想要的信息。Wishlist 服務存儲有關列表的信息以及每個項目的 ID。目錄服務存儲每個商品的名稱和價格,庫存水平存儲在我們的庫存服務中。因此,在我們的 BFF 中,我們將公開一個用於檢索完整播放列表的方法,該方法至少包含 3 個調用:

進行多個下游調用以構建願望清單的視圖

從效率的角度來看,並行運行儘可能多的調用會更明智。一旦對 Wishlist 服務的初始調用完成,理想情況下我們希望同時運行對其他服務的調用,以減少總體調用時間。這種需要將並行運行的調用與順序運行的調用混合在一起的需求很快就會變得難以管理,特別是對於更復雜的場景。這是反應式編程可以提供幫助的領域(例如RxJavaFinagle 的 futures系統提供的),因爲多個調用的組合變得更容易管理。

然而,理解故障模式變得很重要。在上面的示例中,我們可以堅持所有下游調用都必須返回,以便我們將有效負載返回給客戶端。但這明智嗎?顯然,如果願望清單服務關閉了,我們就無能爲力,但如果只有庫存服務關閉了,那麼僅僅降低我們傳遞迴客戶端的功能(也許只是刪除庫存水平指示器)不是更好嗎?這些問題首先必須由 BFF 本身來管理,但我們還需要確保調用 BFF 的客戶端可以解釋部分響應並正確呈現它。

重用和最好的朋友

每個用戶界面只有一個 BFF 的問題之一是,最終可能會導致 BFF 本身之間出現大量重複。例如,它們最終可能會執行相同類型的聚合,具有相同或相似的代碼用於與下游服務交互等。有些人對此的反應是希望將這些重新合併在一起,因此擁有通用聚合 Edge API 服務。這種模型已經被一次又一次地證明會導致代碼高度臃腫,並且將多個關注點擠在一起。

正如我之前多次說過的,我對跨服務的重複代碼相當放心。也就是說,雖然在單個進程邊界中,我通常會盡我所能將重複重構爲合適的抽象,但當面對跨服務的重複時,我不會有相同的反應。這主要是因爲我經常更擔心提取共享代碼可能會導致服務之間的緊密耦合 - 我比一般的重複更擔心這一點。也就是說,在某些情況下這樣做確實是有道理的。

我的同事皮特·霍奇森(Pete Hodgson)指出,當你沒有最好的朋友時,“共同”邏輯通常最終會被融入到不同的客戶本身中。由於這些客戶端使用非常不同的技術堆棧,因此識別這種重複發生的事實可能很困難。由於組織傾向於爲服務器端組件使用通用技術堆棧,因此擁有多個重複的 BFF 可能更容易發現和排除。

當確實需要提取共享代碼時,有兩個明顯的選擇。第一種方法通常是最便宜的,但也更麻煩,是提取某種共享庫。這可能會出現問題的原因是共享庫是耦合的主要來源,尤其是在用於生成客戶端庫以調用下游服務時。儘管如此,在某些情況下,這感覺是正確的 - 特別是當被抽象的代碼純粹是服務內部的一個問題時。

另一種選擇是提取新服務中的共享功能,如果您可以概念化新服務具有圍繞相關領域建模的功能,那麼這種方法可以很好地發揮作用。

這種方法的一種變體可能是將聚合責任推向下游的服務。以上面我們討論的願望清單呈現爲例。假設我們在兩個地方渲染一個願望清單 - Android、iOS Web。我們每個最好的朋友都在發出同樣的三個電話:

多個 BFF 執行相同的任務

相反,我們可以更改 Wishlist 服務來爲我們進行下游調用,從而簡化調用者的工作:

將聚合職責進一步推向下游,以消除 BFF 中的重複

我不得不說,在兩個地方使用相同的代碼並不一定會導致我想要以這種方式提取服務,但如果創建新服務的交易成本足夠低,我肯定會考慮它,或者我在多個地方使用它(例如可能在桌面網絡上)。我認爲,當你要第三次實現某些東西時創建抽象的古老格言仍然感覺像是一個很好的經驗法則,即使在服務級別也是如此。

桌面 Web 及其他領域的 BFF

您可以將 BFF 視爲僅用於解決移動設備的限制。桌面 Web 體驗通常在具有更好連接性的功能更強大的設備上提供,其中進行多個下游調用的成本是可控的。這可以讓您的 Web 應用程序直接對下游服務進行多次調用,而無需 BFF。

我也見過在網絡上使用 BFF 也很有用的情況。當您在服務器端生成 Web UI 的較大部分(例如使用服務器端模板)時,BFF 是可以完成此操作的明顯位置。它還可以在一定程度上簡化緩存,因爲您可以在 BFF 前面放置一個反向代理,從而允許您緩存聚合調用的結果(儘管您必須確保相應地設置緩存控件,以確保聚合內容的到期時間與聚合中最新鮮的內容需要儘可能短)。事實上,我已經看到它被多次使用,但沒有稱其爲 BFF——事實上,通用 API 後端通常是從這樣的野獸中發展而來的。

我見過至少有一個組織將 BFF 用於需要撥打電話的其他外部方。回到我常年使用的音樂商店示例,我可能會公開 BFF 以允許第 3 方提取版稅支付信息、提供 Facebook 集成或允許流式傳輸到一系列機頂盒設備:

使用 BFF 將 API 公開給第三方

這種方法尤其有效,因爲第三方通常沒有能力(或沒有意願)使用或更改他們進行的 API 調用。使用通用 API 後端,您可能必須保留舊版本的 API,以滿足一小部分無法進行更改的外部方的需求 - 有了 BFF,這個問題就大大減少了。

和自治

我們經常看到這樣的情況:一個團隊正在開發前端,而另一個團隊正在創建後端服務。一般來說,我們試圖通過轉向圍繞業務垂直領域的微服務來避免這種情況,但即使如此,在某些情況下這種情況也很難避免。首先,在一定規模或複雜程度下,需要多個團隊參與。其次,執行良好的 Android 或 iOS 體驗所需的技術深度通常需要專門的團隊。

因此,構建用戶界面的團隊面臨着這樣的情況:他們正在調用另一個團隊正在驅動的 API,並且 API 通常在用戶界面開發的同時不斷髮展。BFF 可以在這方面提供幫助,特別是當它由創建用戶界面的團隊擁有時。他們在創建前端的同時改進了 BFF 的 API。他們可以快速迭代兩者。BFF 本身仍然需要調用其他下游服務,但這可以在不中斷用戶界面開發的情況下完成。

使用 BFF 時的團隊所有權邊界示例

使用像這樣沿着團隊邊界對齊的 BFF 的另一個好處是,創建界面的團隊可以更加流暢地思考功能所在的位置。例如,他們可能決定將功能推送到服務器端,以促進未來的重用並簡化本機移動應用程序,或者允許更快地發佈新功能(因爲您可以繞過應用程序商店審覈流程)。如果團隊同時擁有移動應用程序和 BFF,則可以由團隊單獨做出這一決定 - 不需要任何跨團隊協調。

一般周邊問題

有些人使用 BFF 來實現通用的外圍問題,例如身份驗證/授權或請求日誌記錄。我對此很傷心。一方面,大部分功能都很通用,我傾向於使用更上游的另一層來實現它,也許使用 Nginx 或 Apache 服務器層之類的東西。另一方面,這樣的附加層只會增加延遲。BFF 通常用於微服務環境中,由於進行大量網絡調用,我們對延遲非常敏感。此外,您必須部署更多層來構建類似生產的堆棧,這可能會使開發和測試變得更加複雜 - 將所有這些問題放在 BFF 中作爲更獨立的解決方案可能會很有吸引力:

使用網絡設備來實現通用周邊問題

正如我們之前討論的,消除這種重複的另一種方法可能是使用共享庫。假設您的好朋友使用相同的技術,這應該不會太困難,儘管有關微服務架構中共享庫的常見警告適用。

何時使用

對於僅提供 Web UI 的應用程序,我懷疑只有當您在服務器端需要大量聚合時,BFF 纔有意義。否則,我認爲其他 UI 組合技術也可以同樣工作,而不需要額外的服務器端組件(我希望很快會討論這些)。

不過,當您需要爲移動 UI 或第三方提供特定功能時,我會強烈考慮從一開始就爲各方使用 BFF。如果部署額外服務的成本很高,我可能會重新考慮,但 BFF 可以帶來的關注點分離使其在大多數情況下成爲一個相當引人注目的主張。如果構建 UI 的人員與下游服務之間存在顯着分離(出於上述原因),我會更傾向於使用 BFF。

進一步閱讀(和查看)

結論

前端後端解決了使用微服務時移動開發的一個緊迫問題。此外,它們還爲通用 API 後端提供了一個引人注目的替代方案,許多團隊將它們用於移動開發以外的目的。限制他們支持的消費者數量的簡單行爲使他們更容易合作和改變,並幫助開發面向客戶的應用程序的團隊保留更多的自主權。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章