はじめに
Monorepo管理ツールのNxを使用して以前フロントパッケージ開発やクライアントアプリの開発をおこなっています。
monorepoやNxと聞いてちんぷんかんぷんで、そもそものMonorepoとは何か?Nxで何を実現したいのか?を改めて振り返りたいと思います。
- Monorepoの特徴
- Monorepoツールで実現できること
- Nxでの開発体験
以上についてまとめていきたいと思います。
まずググってみた
まずMonorepoをリサーチしてみて人によって定義が色々あり、曖昧だったので代表的なサイトや会社をググって軽くまとめてみました。
Wikipedia
wiki引用
In version control systems, a monorepo ("mono" meaning 'single' and "repo" being short for 'repository') is a software development strategy where code for many projects is stored in the same repository.
バージョン管理システムにおける多くのプロジェクトのコードが同じリポジトリに格納されるソフトウェア開発戦略
monoは「単一」、repoは「リポジトリ」の略
monorepoをwikiってみて単一のリポジトリで管理することは以下の印象でした。
- バージョンの依存関係を一つにまとめることでビルドを最適化できる。(複数のリポジトリがサードパパーティに依存している場合、各リポジトリでバージョンを合わせる必要がある)
- テストやビルド, デプロイなどのCI/CDを単一に行うことや機能やライブラリを共有も容易。
Nrwl(Nx開発会社)
nrwl blog引用
A monorepo is a single repository containing multiple distinct projects, with well-defined relationships.
Monorepoは、複数の異なるプロジェクトを含む単一のリポジトリであり、その関係は明確に定義されています。
各monorepo toolsの特徴やPolyrepoとの対比についての取り上げていました。
こちらでも複数のリポジトリをmonorepoにする事で、バージョン管理やCI上のテスト・デプロイを一貫して通すことのメリットや反対にPolyrepoだと各レポで同じサービスやコンポーネント作成の工数やコマンド・コードの差異があるデメリットを言っていました。
Polyrepoだとチーム間での各リポジトリの把握がmonorepoと比べてしづらい印象や他のリポジトリとの依存関係の面倒も横断して調整に対する負担もかかると思いました。
CricleCI Blog
CricleciブログではマイクロサービスとMonorepoの記載やリポジトリを分割する(Polyrepo:ポリレポ)に対する懸念点にふれていました。
- メリット:単一で見やすいなど、コード共有・リファクタリング・リリース管理も良い印象。
- デメリット:共通ライブラリの変更を行う場合注意が必要
私があまりマイクローサービスに詳しくないのですが、マイクロサービス目線のMonorepo対する誤解(コードの密結合や個々のデプロイ周り)についても記載がありました。
What is Monorepo?
プロジェクトに関わる複数のリポジトリを単一のリポジトリで管理するソフトウェア開発戦略のことをMonorepoという。
またMonorepoとは反対に複数リポジトリを一つひとつ単一に管理することをPolyrepo(or Mulch repo)という。
Monorepoで実現できること・したいこと
色んな代表的な会社の記事を漁ってみてMonorepoで実現できることは以下の内容に落ち着きました。
(ただしプロジェクトによってフロントエンド、バックエンドでの技術選定や開発手法によってよりメリットになり得ることは省いた内容です。)
- コード管理・保守が容易
- 複数レポのGit管理
コード管理・保守・テストが容易
複数レポと比べて単一にすることによって、プロジェクトに関わるコード全体の見やすさやリポジトリを横断してコードを共有することも可能です。
また以上の利点があることによってリファクタリングやバグ改修などの開発体験向上に繋がる。
複数レポが関わる場合の新規機能や機能修正の場合などは開発がしやすいと思います。また複数のモジュールを結合したテストを行う場合においてもバグ発見し修正しやすい印象。
Git管理
Gitの変更追跡・PR履歴も一つのリポジトリでの管理することで変更の把握がしやすい、github actions(CI)でデプロイやテストを一つにまとめれる。
monorepoで開発しづらいこと
現在業務で使用していて開発しづらいこととすれば以下を記載しましたがまだまだ出てきそう。
- package.jsonにコマンドの設定やパッケージがPJ数が多いほど肥大化してしまう。
- CIや環境周りを整備する時は全レポを巻き込み影響範囲がでかいので慎重に行わないといけない。
What is Nx?
高速かつ拡張可能なビルドシステムでmonorepoベースの開発ツールセットです。
様々なFWやライブラリを開発するための機能がプラグインとしてサポートされており、monorepoで複数PJを取り扱う際の便利な機能がいくつか備わっています。
主な利点としては以下になります。
- Nxコミュニティに豊富なプラグインがある。
- モダンな技術スタックでお手軽なプロジェクト生成
- 実行コマンドのキャッシュ
- PJの依存関係の可視化(Nx Graph)
モダンな技術スタックでお手軽なプロジェクト生成
Angular, React, NestJS, Express, node.jsなどに対応しておりworkspaceを立ち上げる際に、どのツールで生成するか選択することが出来ます。
生成時に、様々なCSSツール(SCSSやstyled-components, emotionなど)選択できたり、アプリ名を入力することで、選択したツールを元にapp
とe2e
ディレクトリを生成します。
また、環境に応じてプラグインのサポートが豊富でReactベースのプロジェクトを対象にStorybook
やTailwindCSS
の環境設定もお手軽にできます。
Nx Consoleだとgenerate
項目を表示してくれる。
実行コマンドのキャッシュ
nx affected
コマンドでPRで変更したファイルとその依存関係を計算しキャッシュをしてbuildやtest,lintを行なってくれます。
例えば/client1 ~ 3
のappsがあり、/libs
のutils関数を変更したとします。そこでnx affected --target=build
を実行するとビルド時にutils関数を使用している/client2, 3
は依存関係があるのでビルド対象ですが、/client1
については依存関係がないのでキャッシュされるといった仕組みです。
- キャッシュ場所
/node_modules/.cahce/nx
実際にはnx内部でそういった差分を計算し最終的にビルドするソースをwebpackを使用してビルドを行なっているみたいです。
またCI上にも適用することができるので提供会社が実際にaffected
を使うかどうかでのCI時間を比較していました。
CI rebuild times can be dramatically improved, since any project that has no changed dependencies will just load from the cache instead of re-executing the command. The data for the chart below was taken from two production code bases. This is the power of caching.
・deeplでの翻訳
依存関係が変更されていないPJは、コマンドを再実行する代わりにキャッシュからロードするだけなので、CIの再構築時間は劇的に改善されます。
Nx Communty
Nx上でアプリケーションやライブラリをスムーズに扱えるようプラグイン開発しているコミュニティがあります。
viteやstorybook, firebaseの初期設定などあります。
実際に開発するのはプラグインを開発できるnx/devlit環境を生成することで作成できます。独自のプラグインを共有するには、npmレジストリに公開することで可能です。(詳しくはこちらになります。)
Nx Console
GUIベースでNxコマンドを実行することができるVSCode上の拡張機能です。
build
やtest
はもちろん、環境に応じて生成が可能なgenerate
コマンドでGUIの操作が可能なNx CLI
をサポートしています。
参考記事
まとめ
今回monorepoでの開発をするに至って複数のリポジトリを一つにまとめることによってコードの把握や共有、バージョン管理を容易にしている効果があることはわかりました。
また、上記の開発体験をサポートしているNx(monorepo tool)を今回探ってみて、Nxコマンドやjsonファイルの設定などNxに対する学習コストが高い印象でした。
またNx console
でモダンな技術に対するサポートや開発初期の環境構築はしやすいと思いました。