avatarHannah Lin

总结

介绍前端工程师越来越喜欢使用 Monorepo 架构,并阐述了 Mono repo 的优缺点。

摘要

Monorepo 概念雖然有一段歷史了,但这个名詞卻是近幾年才變得如此熱門。Monorepository (簡稱 Monorepo) 概念雖然有一段歷史了,但這個名詞卻是近幾年才變得如此熱門。自己公司也是這半年才導入這個架構,再嚐到許多甜頭後想寫一篇來介紹它,希望多一點人認識此架構。這篇並不會有程式碼,而是從現有架構痛點開始 (Single Repo Monolith、Multi-repo)、為什麼要用 Monorepo 等,若想要實際導入可以參考 nx.dev。

观点

  • Monorepo 可以解决 Multi repo 的痛点,因为只有一个 Repo 所以管理起来很方便,一个 webpack、一个 test suite runner、共用 dependency 若有变动,那有用到此 dependency 的 project 都会知道,并且因为只有一个 repo 所以大家都是使用最新的 code 不会用更新不及时的状况。
  • Mono repo 的优点包括统一管理 configs and tests,部署时间很快,管理 dependency 变得很容易,能重复利用 share code (libs),编译时间不会变慢。
  • Mono repo 的缺点包括 codebase 龐大,所有人都可以改动别的 team 的 code,team 跟 team 之间要划分清楚程式码不能互相引入。
  • 想要導入 monorepo,自己是蠻推薦 Nx,官方文件寫的很清楚也搭配一些影片教學。
  • 当然,并不是每一个案子都适合使用 monorepo 管理,还是要針對專案内容选择合適的架构,但总体而言若專案夠龐大、又有不同團隊處理不同項目 monorepo 就蠻適合的。

為什麼前端工程越來越愛使用 Monorepo 架構

Using single git repository to manage multiple apps and libraris

toptal.com/front-end/guide-to-monorepos

Monorepository (簡稱 Monorepo) 概念雖然有一段歷史了,但這個名詞卻是近幾年才變得如此熱門。自己公司也是這半年才導入這個架構,再嚐到許多甜頭後想寫一篇來介紹它,希望多一點人認識此架構。這篇並不會有程式碼,而是從現有架構痛點開始 (Single Repo Monolith、Multi-repo)、為什麼要用 Monorepo 等,若想要實際導入可以參考 nx.dev

現有架構遇到了什麼問題 ?

20 年前的前端相當單純,但在這 6 ~7 年卻產生巨變,現在應該沒幾個人只用純 html/css/js 做出網站了,大部分都是以前端框架出發搭配一大堆的 dependencies (SCSS preprocessors、task managers、npm、typescript…) 。當團隊發展到一定規模又會分出好幾個不同產品,每一個產品使用的 dependencies 都不大相同使得維護變得困難,到底要怎麼做到避免重覆程式碼以及分好責任歸屬 呢?直接用實際例子會清楚許多

若 shop.com 公司今天有數個專案,每一個專案都有不同負責的團隊

購物網站 shop.com (React)
結帳 shop.com/cart
購物網站手機版 m.shop.com (不是 RWD,是獨立網站)
後台 admin.shop.com (Vue)
分析使用者網站 analytics.shop.com (Angular)

Note. 雖然不同專案你可以選擇用不同前端框架,但若都用同一種會看到 monorepo 的更大好處。

這張圖我修了三個小時 = =,請幫我停留 10 秒鐘 XD

shop.com 跟 shop.com/cart 雖然是在一個 domain 底下,但負責的卻是完全不同的團隊

在這種複雜的架構下,我們比較熟悉的方法為 Monolith 跟 Multi repo [延伸閱讀: 初探 Micro Frontends 程式架構]

Single-repo Monolith 😰

最開始大家都是使用這種架構開發的,但隨著前端工程日益複雜,前端需要做的事越來越多、更多 dependencies 被引入,在 Single-repo Monolith 架構下整包會變超級肥大,部署時也必需要整包一起,想當然爾 deploy 時間都爆久,這樣的架構缺點很明顯,也無法達到高擴充性與高效率的組織開發。

Single-repo Monolith 
apps
  ├ node_modules
  ├ libs // 放 share 的東西
  ├ design-systems 
  | ├ node_modules
  | ├ xx...
  ├ shop 
  | ├ node_modules
  | ├ cart
  | | ├ xx...
  | ├ xx...
  ├ shop-mobile
  | ├ node_modules
  | ├ xx...
  ├ admin
  | ├ node_modules
  | ├ xx...
  ├ analytics
  | ├ node_modules
  | ├ xx...
  ├ e2e
  | ├ xx...

Multi repo 😢

現在大部分的前端會採用 Multi repo,每一個獨立案子都會有相對應 repo,團隊各自維護自己 product,釐清責任也很容易

Design-systems Repository
design-systems
  ├ node_modules  
  ├ e2e
  ├ xxxx
Shop Repository
shop 
  | ├ node_modules
  | ├ e2e
  | ├ xx...
Shop Cart Repository: Cart 因為隸屬另一個團隊所以會拆分成兩個 Repository
shop-cart
  | ├ node_modules
  | ├ e2e
  | ├ xx...
Mobile Version Repository
shop-mobile
  | ├ node_modules
  | ├ e2e
  | ├ xx...
Admin Repository
admin
  | ├ node_modules
  | ├ e2e
  | ├ xx...
Analytics Repository
analytics
  | ├ node_modules
  | ├ e2e
  | ├ xx...

看似不錯,但問題又來了

重覆配置 Multi repo 因為每一個 repo 都是獨立的,所以必須建自己的 webpack、開發環境、如何 deploy 等等,若十個 repo 就要維護這 10 個配置,若 repo 之間都不一致,那管理很麻煩

共用程式碼維護成本變很高 projects 間許多邏輯是重覆的,但因為不同 repo,所以在 debug 時就要一次修五份,維護耗時耗力成本相當高。你可能會想那把會重覆用到的東西另外拉出來單獨創一個 libs Repository,直接改 libs 得東西即可,那流程就會變成改

  • libs Repository,version 從 1.0 到 1.1 2.
  • shop、shop-mobile、admin、analytic 安裝最新的 libs 1.1
  • commit 變動,再 push 更新個自 repo,等待 deploy

這也是 Multi repo 最常被抱怨的事,因為 Repo 被切得太清楚,導致只是 share code 小改動流程也超複雜,所花時間也相對變很高

dependencies 的版本管理變異常複雜 每個 team 用的 dependencies 有些相同有些不同,例如 design-system 用了 react 17.0.2 而 shop 還在 15.6,若新版本捨棄某些支援,就會產生 bug。或是因為沒有及時 pull 最新的 libs Repo 所以更新不及時而產生 bug。

Mono repo 😊

One repository, Multiple Project, Shared libraries

Mono repo 可以解決上面 Multi repo 的痛點,由於只有一個 Repo 所以管理起來很方便,一個 webpack 、一個 test suite runner 、共用 dependency 若有變動,那有用到此 dependency 的 project 都會知道,並且因為只有個 repo 所以大家都是使用最新的 code 不會用更新不及時的狀況

apps
  ├ design-systems
  ├ design-systems-e2e
  ├ shop
  | | ├ cart
  ├ shop-e2e
shop-mobile
shop-mobile-e2e
  ├ admin
  ├ admin-e2e
  ├ analytics
  ├ analytics-e2e
node_modules
libs
  ├ utils

libs 下面可以放任何會被共用的東西,例如 design systems、TS Interfaces、JS Utilities 等等。這樣架構其實各自團隊還是只專注自己的案子裡,例如 shop-mobile team 只會改這個 folder 裡的東西,build 時也可以單獨跑 shop-mobile only。

雖然 monorepo 是主打 一個 repo,一個 package.json,但自己公司還是會把一些 team specific 的 package 放到自己的 folder 裡面,例如很確定只有 design-system 有用到 gatsby。所以這邊設置還是可以針對自己公司做調整,但若這個 package 未來很有可能被別的 team 用,最好也是放到最外層。

apps
  ├ design-systems
  | ├ node_modules
  ├ design-systems-e2e

優點

  • 統一管理 configs and tests: 因為只有一個 repo 所以不需要再重覆配置環境,包括 CI/CD、unit、e2e、webpack 都只需要維護一份就好
  • 部署時間很快: 雖然是同一個 repo 但可以針對不同案子設定 CI/CD 個別部署。若 share code 有變動,你不需要 pull request multi-repo 的每一個專案,而是只要去更新有用到此 share code 的案子即可。
monorepo 會偵測哪個 project 有用 lib 的變動然後去更新它
  • 管理 dependency 變很容易: 一個 repo一個 package.json
  • 能重覆利用 share code (libs)
  • 編譯時間: 使用 monorepo 編譯時間並不會變慢,因為使用好的 monorepo 工具 (例如 nx) 都幫你做好緩存跟效能優化了

當然,monorepo 還是有一些缺點

  • codebase 龐大
  • 所有人都可以改動別的 team 的 code: 因為只有一個 repo,但自己認為不是大問題因為把 code push 上去都需要經過 pull request 審核,若隨便都被 approved 那就是教育訓練不足了…
  • team 跟 team 之間要劃分清楚程式碼不能互相引入: 只有 libs 跟 design system 裡的 code 是可以 share 的,不然會發生很多意外的 bugs。
// shop.js
import 'adminTools' from 'admin'

想要導入 monorepo,自己是蠻推薦 Nx,官方文件寫的很清楚也搭配一些影片教學

小結

當然並不是每一個案子都適合使用 monorepo 管理,還是要針對專案內容選擇合適的架構,但總體而言若專案夠龐大、又有不同團隊處理不同項目 monorepo 就蠻適合的

Reference

Frontend
Monorepo
Recommended from ReadMedium