Explain Monorepo

故事化直白的解釋 Monorepo 是什麼

前言

隨著前端的複雜程度增加,Monorepo 這個詞彙也時常出現在眼前,可惜的是相關介紹資源非常不足,趁著近期較為閒暇的時間來統整紀錄一下我對 Monorepo 的理解。受到不同好文章與影片的的啟發,我想寫一篇最直白的 Monorepo 解釋,希望能夠幫助更多人理解 Monorepo 的概念。

什麼是 Monorepo?為什麼需要它?

要了解 Monorepo 可以先從分析現有解決方案存在的問題點,再來延伸至 Monorepo 的解決方案。

Multi-Repository / Polyrepo(多儲存庫)

當創建一個新專案時大多時候直覺的想法是「再創立一個新的 Git 儲存庫」,這樣的做法在專案規模小的時候並不會有太大的問題,但隨著專案規模的增大卻會衍伸出不少麻煩,像是不同的應用使用到相同的邏輯、型別、設定檔或套件時都會發生難以統一管理或更新的問題,如果要同步儲存庫共用的程式片段會需要特地發布套件,再由其他專案透過套件管理工具進行追蹤更新。

這種作法並沒有任何問題,只是顯然並不適合作為更新頻繁專案的解決方案:

  • 需要特別發布套件
  • 每次套件更新都要對「任何」依賴的項目更新
  • 需要管理「所有」儲存庫的套件版本
  • 需要在不同的儲存庫之間切換
  • CI 流程較不方便
  • 回朔系統狀態困難

Monolith Repo(單體儲存庫)

換個角度,如果架構上所有相互依賴的儲存庫都放在相同的儲存庫當中不就沒有問題了嗎?這樣的做法便是:「把任何專案都塞到同個儲存庫就完事」。 雖然有點反直覺,但確實解決了多儲存庫帶來的問題!但這樣的做法也會導致整個專案龐大且牽一髮動全身,並且對技術選擇僵化缺乏彈性(由於共用相同的套件管理)。

最終還是有擴張性、維護性和靈活度的挑戰:

  • 代碼相互牽連可能導致合併衝突和協作問題
  • 任何改動都需要重新測試或部屬整個專案
  • 套件管理缺乏彈性
  • 專案龐大造成效能問題
  • 權限管理問題

Monorepo(單一儲存庫)

Monorepo
├── apps
│ ├── frontstage
│ │ └── package.json
│ ├── backstage
│ │ └── package.json
│ ├── ui
│ │ └── package.json
│ └── design-system
│ └── package.json
└── package.json

與 Monolith Repo 很像,同樣是「把任何專案都塞到同個儲存庫就完事」特別容易與 Monolith 的做法搞混,差異在於每個儲存庫在套件管理器當中還是視為獨立的專案,這點可以透過套件管理器相關設定🔗或是透過像是Nx 透過 TypeScript Project References🔗

這麼做主要可以改善套件管理缺乏彈性的問題,將套件共同的依賴管理在根目錄當中,但也同時允許每個專案有自己的獨立套件,至於擴張性、維護性和靈活度的挑戰還是要依靠其他工具來解決 😑。

Monorepo 延伸

基於 Monolith Repo 與 Monorepo 都有效能、權限、協作……等問題,因此通常會透過其他工具來解決這些問題,像是:rush🔗Nx🔗Turborepo🔗,透過團隊共享快取避免重複執行、平行化執行、自動化依賴更新各式各樣的功能來強化體驗。

總結

Monorepo 優勢

  • 易於共享代碼
  • 簡化依賴管理
  • 微小更動變得容易
  • 易於大型代碼重構
  • 易於團隊跨專案合作
  • 統一 CI 流程

Monorepo 劣勢

  • 額外學習與維護成本
  • 效能問題
  • 權限問題

Monorepo 與 Monolith Repo 的差異在於多套件管理的彈性,如果專案間頻繁的有需要共用的程式片段或套件,那麼 Monorepo 是一種不錯可以考慮的解決方案,但如果專案間沒有太多關聯使用 Monorepo 反而會增加不必要的複雜度,考慮使用多儲存庫的方式。

延伸閱讀