什么是 Monorepo??
monorepo是具有多個相關服務、項目和組件的單個存儲庫,不同的團隊可以使用它來存儲相關或不相關項目的代碼。monorepo 一詞源自 mono,意思是單個,而 repo 是存儲庫的縮寫。
Monorepo 的好處
以下是使用 monorepo 的一些主要好處:
- 代碼共享:? 項目共享標準代碼、庫或實用程序。
- 可重用性:組件需要在不同的項目中重用。
- 更輕松的代碼審查:代碼審查在 monorepo 中更加高效,因為審查者可以輕松查看相關項目之間的更改上下文,這可以提高代碼質量并更早發現潛在問題。?
- 簡化 CI/CD 流程:使用單一存儲庫同時發布多個項目變得更加簡單。
- 一致的依賴關系管理:項目共享相似或重疊的依賴關系,并且您希望集中管理依賴關系。
- 團隊協作:從事相關項目的團隊可以在單一存儲庫中更有效地協作,共享知識、見解和資源。
- 微服務架構:?在處理一組密切相關的微服務時,單一存儲庫可以簡化跨服務的代碼共享、依賴關系管理和測試。
- 版本一致性:?所有項目都可以共享標準版本控制架構,從而簡化溝通和理解。
專門設計用于使用 Node.JS 管理 Monorepos 的庫和工具
- Lerna:一種廣泛使用的工具,用于管理具有多個包的 JavaScript 項目。
- Nx:Nx 專注于 angular 但適用于其他框架,為 monorepos 提供強大的開發工具,強調高效的工作流程、代碼重用和測試。
- yarn 工作區:Yarn 的內置 monorepo 功能允許在單個代碼庫中管理多個包。
- Rush:微軟開發的可擴展的 monorepo 管理器,適用于大型代碼庫。
- Bolt:一種單一存儲庫管理工具,專注于性能,對于某些操作可以比 Lerna 更快。
- Monorepo Manager:該工具簡化了 monorepo 的創建和維護,提供了一個用戶友好的界面來管理包、依賴項和腳本。
- pnpm:與 Yarn 一樣,pnpm 還通過其工作區功能支持 monorepo 設置,通過共享依賴項減少重復并提高磁盤空間利用率。
每個工具都提供特定的優點和功能,因此選擇取決于您的項目的要求和偏好。
為什么是Lerna?
Lerna是一個設計用于管理包含多個 npm 包的存儲庫的工具。它簡化了在單個git存儲庫中的多包存儲庫中處理依賴項、發布和發布包的過程。Lerna 對于 monorepo 特別有用,因為它可以在同一存儲庫中處理不同 npm 包的開發人員之間實現高效的代碼共享和協作。它允許開發人員將具有多個包的項目視為單個實體,從而改進開發生命周期管理。
安裝 Lerna 之前的先決條件
- Git:下載并安裝Git
- Git bash(終端):如果您使用的是 windows,Git Bash 包含在 Git 安裝中;對于 macos 和 linux,請使用系統的終端。
- node.js:下載并安裝Node.js
- npm:npm 包含在 Node.js 中,因此一旦安裝了 Node.js,npm 就可以在您的終端中使用。通過打開終端并輸入進行驗證。npm -v。
我們正在創建一個單一存儲庫,其中包括后端服務器使用的支付服務。此外,后端服務器和支付服務將共享日志服務。
- 日志服務:?專為跨各種服務進行高效日志記錄而設計。
- 支付服務:?負責處理支付相關功能。
- 后端服務器:?執行支付處理并集成日志服務以實現無縫操作。
現在讓我們深入研究使用 Lerna 實現 Monorepo。
第1步:創建目錄并初始化Lerna
導航到項目的根目錄并初始化 Lerna:
mkdir?monorepo?#?create?a?repo?Directory?of?the?monorepo? cd?monorepo npx?lerna@latest?init?#?initalize?the?repo
上面的npx命令將創建一個新的 Lerna 管理的存儲庫。
lerna.json:配置文件包含Lerna行為的設置,例如版本控制模式、包位置等
package.json:整個存儲庫的根 package.json 文件。
git?config?user.name?${username} git?config?user.email?${email}
第2步:生成后端包
確保您位于項目的根文件夾中。?
用于創建包的 Lerna 命令:npx lerna create #{packageName} #{directory}
這里,目錄是默認的:packages
npx?lerna?create?back-end? //or //this?will?skip?the?questionnaire?related?to?package npx?lerna?create?back-end?-y
上面的命令,不帶-y? 會提示你各種問題,比如覆蓋包名、添加描述等等。但是,這些詳細信息對于本示例來說并不重要,因此請按“enter”鍵。
運行后,包后端將如下所示:
步驟3:生成支付和日志服務包
再次執行相同的過程,但指定要創建的服務的目錄,因為我們希望包位于該“services/”目錄中。
在根package.json文件中,您還必須告知 Lerna 目錄中的包services/。編輯package.json工作區配置并添加“services/*”到其中。配置應類似于以下內容:
在根級別的主package.json文件中,您必須告知 Lerna services/ 目錄中的軟件包。修改工作區配置package.json并包含“services/*”.?配置應如下所示:
npx?lerna?create?payment?services?-y npx?lerna?create?Logging?services?-y
第 4 步:設置日志服務
在該目錄中,通過簡單的配置使用 Bunyan 庫services/logging設置日志記錄服務。
-
在日志服務中安裝 Buyan 庫,并將 Mocha 作為開發依賴項安裝在根目錄中,以測試所有服務。
//?root?folder?install?test?dependencies npm?install?mocha?--save-dev? //inside?logging cd?services/logging npm?install?bunyan
-
替換日志功能文件的內容services/logging/lib/logging.js
const?bunyan?=?require('bunyan'); const?logger?=?bunyan.createLogger({ ??name:?'my-logging-service', ??level:?'info', }); module.exports?=?logger;
- 日志記錄測試用例(測試記錄器):
- 替換測試文件的內容services/logging/__tests__/logging.test.js
const?loggingService?=?require('../lib/logging');?//?Import?the?logging?service describe('Logging?Service',?()?=>?{ ??it('should?log?messages',?()?=>?{ ????loggingService.info('Test?log?message'); ??}); });
- 更新 services/logging 的 package.json 中的測試腳本。
"test":?"mocha?./__tests__/logging.test.js"
- package.json 應如所附圖像所示。
- 是時候使用 lerna 運行測試了npx lerna run test –scope=”logging”
- 日志服務實現現已就位,讓我們開發支付服務。
第5步:設置支付服務
付款服務具有一個名為 的函數makePayment,它接受單個參數作為金額并利用記錄器服務來記錄活動。
在services/payment目錄內,并通過簡單的功能設置支付服務。
- 將現有腳本替換為 mocha,提供用于測試目的的代碼片段。
- 要在支付服務中使用日志記錄服務,請將其依賴項添加到支付服務的 package.json 中,如下所述。然后,npm i在services/payment目錄中運行進行安裝。
"scripts":?{ ????"test":?"mocha?./__tests__/payment.test.js" ??}, ??"dependencies":?{ ????"logging":?"file:../logging" ??}
- package.json應該如圖片所示
替換支付文件的內容。services/payment/lib/payment.js
const?loggingService?=?require('logging'); const?paymentService?=?{ ??makePayment:?(amount)?=>?{ ????loggingService.info('Payment?processing?initiated'); ????//?Implement?payment?logic?here ????loggingService.info('Payment?processed?successfully'); ????return?`Payment?of?${amount}?processed?successfully`; ??}, }; module.exports?=?paymentService;
- makePayment支付服務功能測試用例。
- 替換測試文件的內容services/payment/__tests__/payment.test.js
const?chai?=?require('chai'); const?paymentService?=?require('../lib/payment');?//?Import?the?payment?service const?expect?=?chai.expect; describe('Payment?Service',?()?=>?{ ??it('should?make?a?payment?successfully',?()?=>?{ ????const?paymentResult?=?paymentService.makePayment(100); ????expect(paymentResult).to.equal('Payment?of?100?processed?successfully'); ??}); });
- 是時候使用 lerna 運行測試了npx lerna run test –scope=”payment”
- 我們已經完成了支付服務的實施。現在,讓我們繼續創建后端服務。
第 6 步:設置后端服務器
我們將使用基本的 GET API 配置服務器,該 API 利用記錄器和支付服務。此設置將有助于付款和記錄相應的活動。
- 安裝 express 服務器并實現使用這兩種服務的功能。
//from?root cd?packages/back-end npm?install?express
- 替換日志功能文件的內容packages/back-end/lib/back-end.js
- 我們想在服務器中使用支付和日志服務,所以讓我們在package.jsonof的依賴項中添加以下代碼片段packages/back-end?
"logging":?"file:../services/logging", "payment":?"file:../services/payment"
- 替換腳本塊來運行服務器并進行測試,如下所示:
"scripts":?{ ????"start":?"node?./lib/back-end.js", ????"test":?"mocha?./__tests__/back-end.test.js?--exit" }
- package.json應該如所附圖片所示
- 現在,通過執行更新依賴項npm update。
- 將 的內容替換packages/back-end/lib/back-end.js為以下代碼:
- 我們將創建一個帶有get / API端口的服務器3000,并使用日志記錄和支付服務。?
const?express?=?require('express'); const?loggingService?=?require('logging'); const?paymentService?=?require('payment'); const?app?=?express(); app.get('/',?(req,?res)?=>?{ ??//?Use?of?logging?service ??loggingService.info('Backend?server?received?a?request'); ?? ??//?Use?the?payment?service ??const?paymentResult?=?paymentService.makePayment(100); ??loggingService.info('Payment?result:',?paymentResult); ??res.send('Backend?Server:?Running!'); }); app.listen(3000,?()?=>?{ ??console.log('Backend?server?is?running?on?port?3000'); });
- 安裝chai-http以對目錄上的 API 進行單元測試packages/back-end。
- ?npm i chai-http –save-dev?
- 替換測試文件的內容,我們將在其中測試 API 是否按預期工作。
const?chai?=?require('chai'); const?chaiHttp?=?require('chai-http'); const?app?=?require('../lib/back-end');?//?Import?the?Express?app //?using?request?server?as?chaiHttp; chai.use(chaiHttp); const?expect?=?chai.expect; describe('Backend?Server',?()?=>?{ ??it('should?log?a?request?and?process?payment',?(done)?=>?{ ????chai.request(app) ??????.get('/') ??????.end((err,?res)?=>?{ ????????expect(res).to.have.status(200); ????????expect(res.text).to.equal('Backend?Server:?Running!'); ????????done(); ??????}); ??}); });
- 恭喜!這樣就完成了 monorepo 中三個微服務的簡潔有效的實現。?
第 7 步:運行應用程序
- 偉大的!現在,讓我們啟動服務器并觀察所有服務如何組合在一起并工作。
- lerna run start在根文件夾中執行。這將在端口 3000 上啟動服務器。打開瀏覽器并導航至localhost:3000/。您將觀察到輸出“后端服務器:正在運行!”?顯示在瀏覽器中。
- 檢查終端中的日志,您將遇到類似于圖中所示的結果。
- 執行lerna run test,這將運行所有微服務中的所有測試,因為所有微服務test在腳本中都有命令。
結論
創建具有后端服務器、支付服務和日志記錄服務的 monorepo 凸顯了統一開發方法的好處。此設置通過將相關組件整合到單個存儲庫中來促進高效的代碼管理和共享。
將日志記錄服務集成到支付服務和后端服務器中體現了代碼可重用性和跨服務的一致日志記錄實踐的強大功能。
采用 monorepo 架構會帶來一個有組織且協作的開發環境。模塊化簡化了開發,提高了效率和長期維護。它為復雜的應用程序提供了堅實的基礎,具有透明的通信、代碼可重用性和有效的測試。