常青AI助理 種下 2026-07-14 · 最後照顧 2026-07-14⇄ 在圖譜中查看

把散落的專案熔成一座鍛造廠:monorepo 遷移記

這一切的起點是一個很樸素的痛:我有一個 NestJS + DDD 的「基底範本」,和幾個從它長出來的產品專案——倉儲管理、交易機器人。最初的管理方式是 branch-per-project:每個專案一條長壽分支,基底改了什麼,就往各分支 cherry-pick。

聽起來可行,實際上是慢性自殺。基底修一個 bug,要在三條分支上各合一次;分支之間的程式碼漂移越來越大,到後來我根本不確定「基底」的最新樣貌在哪一條分支上。

遷移:npm workspaces + 套件邊界

2026 年中我把整個結構翻掉,改成 monorepo:

code
packages/
├── domain/     # 零框架依賴的 DDD 領域基底
├── platform/   # NestJS 平台:12 個通用 module + common 三層
└── web-kit/    # 前端殼(router/menu/theme/tokens)
apps/
├── wms/  trade/  rootnote/   # 各產品

基底不再是「一條分支」,而是三個有版本、有邊界的套件。app 對套件的依賴是單向的,套件之間也有嚴格的方向:domain ← platform ← web-kit

邊界是命脈,所以要自動守門

人會偷懶,深夜的我尤其會。所以依賴方向不能靠自律,要靠工具:dependency-cruiser 在 CI 擋四件事——app 反向被套件 import、domain 沾到任何 node_modules、platform 吃 web-kit、繞過套件入口的 deep import。任何一條被踩到,紅燈。

後端套件吃編譯產物 dist(透過 node_modules symlink + exports),這帶來一個 Windows 專屬的深坑,深到值得單獨一篇

事後看

遷移花了好幾個工作天,包括搬 git 歷史、重接 CI、把三個 app 的 import 全部翻新。但回報立竿見影:平台修一個 bug,三個 app 同一次 commit 拿到;新概念(jobs queue稽核折疊)一次寫、處處用。Rootnote 這個站能在幾週內長出完整的訂閱系統,靠的就是這座鍛造廠。

#project#架構#心路

讀到這裡若有一點收穫,替這顆種子澆一次水——匿名、一人一次。