在 VIPER(View·Interactor·Presenter·Entity·Router)篇中,我曾用一段話帶過「Uber 受到 VIPER 啟發而打造的 RIBs」,這次就來完整介紹。在韓國,RIBs 也因 Toss 採用而廣為人知。
理解 RIBs 的關鍵只有一個。到目前為止介紹的所有架構(MVC·MVVM·VIPER·ReactorKit)都以畫面作為基本單位。RIBs 捨棄了這個前提,改以商業邏輯為單位拆分 App。
無畫面元件的存在
RIBs 是 Router·Interactor·Builder 的縮寫。這三者是每個元件(RIB)的必要組成,只有在需要時才會加入 Presenter 和 View。
- Interactor:商業邏輯的核心,也是 RIB 的大腦。
- Router:透過 attach 和 detach 子 RIB 來管理樹狀結構。
- Builder:組裝一個 RIB,並注入相依性。
- Presenter·View:選用項目,只存在於需要畫面的 RIB。
「View 是選用項目」這點至關重要。以叫車 App 的「乘車中」狀態為例,它涵蓋地圖畫面、司機資訊卡和付款準備邏輯,但本身不是某一個特定畫面。在 RIBs 中,可以把它做成無畫面的 viewless RIB,再將有畫面的子 RIB 掛在其下。
整個 App 會成為 RIB 的樹狀結構。登入狀態、乘車狀態等App 的狀態變化,就是樹狀結構的變化。登出時,LoggedIn RIB 會從樹中完整移除,其下的畫面和邏輯也一併消失。狀態管理也就是樹狀結構管理。
為什麼只有超大型組織會使用?
RIBs 從一開始的設計目標就是「數百人共同開發一個 App」。Uber App 有數十個團隊同時提交程式碼,只靠畫面單位的分離無法建立團隊邊界,因為同一個畫面會混入多個團隊的邏輯。
以 RIB 為單位拆分後,每個團隊都能擁有自己的 RIB 子樹。Builder 強制規定相依性注入的位置,因此根本無法碰觸其他團隊 RIB 的內部。這是在架構層級實現模組化系列提到的「強制邊界」。
Toss 導入 RIBs 的原因也相同。在一個 App 包含數十種金融服務的超級 App 結構中,讓每項服務成為一個 RIB 子樹後,掛接和移除都會很明確。
反過來說,這些優點在小型團隊中都會變成成本。
第一,樣板程式碼比 VIPER 還多。一個 RIB 基本上會產生四到五個檔案,因此 Uber 也提供程式碼產生範本。
第二,學習曲線很陡。團隊必須學會樹狀結構設計、attach/detach 時機,以及依範圍管理的相依性注入,才能建立共同理解。
第三,它和以畫面為中心的思考方式不一致。規格通常以畫面為單位,而 RIBs 以狀態拆分,因此在設計階段需要進行轉換。
它和 VIPER 有什麼不同?
只看組成元件名稱,可能會以為它是 VIPER 的變體,但兩者的差異是根本性的。
| VIPER | RIBs | |
|---|---|---|
| 基本單位 | 畫面 | 商業邏輯 |
| 無畫面元件 | 不可能 | viewless RIB |
| 導覽 | Router 負責畫面切換 | Router 負責樹狀結構的 attach/detach |
| 目標 | 分離單一畫面的責任 | 分離組織單位的程式碼所有權 |
VIPER 的 Router 負責「如何前往下一個畫面」,RIBs 的 Router 則負責「在目前的 App 狀態下,哪些邏輯元件應該存活」。名稱相同,問題不同。
總結
- RIBs 是 Uber 的架構,將 App 以商業邏輯單位拆成樹狀結構,而不是以畫面拆分。
- RIB 可以沒有畫面,並以樹狀結構的 attach/detach 表現 App 狀態變化。
- 它的目標是團隊別的程式碼所有權與強制邊界,因此在數百人規模的組織中最能發揮價值。Uber 和 Toss 是代表案例。
- 對小型團隊而言,樣板程式碼和學習曲線會壓過優點。選擇標準就如第 8 篇的選擇指南所述,團隊規模是大部分答案。
至此,iOS 架構系列中只提過但尚未深入介紹的部分也全部補齊了。從 MVC 到 RIBs,請記住所有架構最終都是對同一個問題「邏輯應該放在哪裡」提出的不同答案。

![[iOS 架構 #10] Uber RIBs:以商業邏輯拆分 App 封面圖](/assets/images/posts/f06929f4-621d-40e7-88d9-8817b02ef56d/uber-ribs-architecture-hero-1.jpg)