Monorepoとは?
Monorepoとは一言でいうと、そのプロダクトに関するコードをすべて単一のリポジトリで管理する方法
です。
なぜ Google は全てのコードをシングルレポジトリを管理しているのかを見ればわかる通りGoogleをはじめ、facebook、Twitterなどの世界の名だたるテックカンパニーでも採用されている手法です。日本ではメルカリグループのソウゾウが導入していますね。
今回解説する内容
- 流行り出した背景
- 解決する課題
- Monorepo達成への道
- 最近のアーキテクチャとの関係
流行り出した背景
マイクロサービス アーキテクチャの普及につれて、それぞれのサービスは解決すべき課題にあったツールや言語を使用し、それらを書く開発チームが独立したリポジトリで管理していくことがスタンダードとなってきました。(いわゆる Polyrepo)
これによりマイクロサービスを迅速に実装し、担当者が別々にデプロイを行なうことで、各サービスや開発チームの独立性を保ったまま、ソフトウェア開発のスピードを大きく高められます。
(実際は解決するビジネス領域単位で、このビジネス領域のサービスはxx言語で開発するといったようなすみ分けがされることが多い。)
しかし、無限に増え続けるマイクロサービスとそのリポジトリを運用していく中で非機能的な横断対応
という点からマイクロサービスとリポジトリを1:1で管理することに無理がたたるようになってきました。
例えばOSSや社内ライブラリ等のバージョンアップや、セキュリティアップデートがこれに該当します。
また、全体として管理対象のリポジトリが多いことは単順にプロダクトとしてのコードベースの可視性が悪く、「ビジネスとしてどのコンポーネントに影響があるか?」「どのコンポーネントをリリースしなければいけないのか?」といった迷いを生んでしまいます。
そこで、これらの問題解決のために提唱されたのがMonorepoです。
もう一度丁寧にMonorepoを一言でいうと、マイクロサービス単位に分割された各資源をその分割状態を維持したまま単一のリポジトリで管理する方法
です。
解決する課題
Monorepoのメリット
メリットとしては、上述したPolyrepoの横断対応に関する問題点の解決が行えることです。
管理する対象が一つになったことで、施さなければいけない対応やプロダクト全体の見通しが良くなります。
他にもプロダクトコードにまつわる周辺ツールの管理もしやすいなどと、プロダクト全体で共通するものの管理に関する問題が解消されます。
実際にBabelでは、実際にモノレポでプロジェクトを管理することによって、以下のようなメリットがあると述べられています。
メリット
- 単一のlint、ビルド、テスト、リリースのプロセス
- モジュールをまたいだ変更がしやすい
- issueを報告する場所が一つ
- 開発環境のセットアップが容易
- モジュールをまたいだテストが一緒に実行されるため、複数のモジュールにまたがるバグがより簡単に見つかる
バージョンアップ対応に関しても、今まではxxというライブラリを使用しているコードを無数のリポジトリから探し出して対応する…といった人海戦術をしなければなりませんでしたが、
そもそも単一リポジトリとなる事で、そのリポジトリ内でで一元管理している依存関係をアップデートしてあげれば一瞬で、使用している全サービスへの反映ができてしまいます。
Monorepo達成への道
Monorepoを実現する周辺技術
ここまででマイクロサービスでの開発をするプロジェクトでMonorepoを選択する理由やメリットがなんとなくわかってきましたが、Monorepoはただコードを単一リポジトリで管理すればいいという考えではありません。
なにも考えずにコードの単一管理をモチベーションにしても、今度はマイクロサービスの利点がこ活かせなくなります。
CI/CDなんかが例としてとりあげやすいですが、何も考えずにリポジトリ単位でパイプラインを構築すると、全サービスに対する各種処理(テスト。ビルド等)が走ってしまって単一の重いパイプラインになってしまいます。
Monorepoはあくまで「マイクロサービス化によるコード管理の複雑化」に効果を発揮させることが大半の目的なため、これではその実現のためにマイクロサービスの利点である「独立したデプロイ」「コンテナ技術を生かした細かいスケーリング」…等が損なわれて本末転倒です。
Monorepoの目指すべき姿は、ソース管理は単一で、デプロイラインは複数
です。
要はコード管理のみを一点に集約し、実際にテスト、ビルド、デプロイされるものは従来通りサービスごとに独立して行える状態である必要があるってことです。
これを実現するために、パイプライン側でテスト、ビルド、デプロイに関するステップをリポジトリで管理されている各サービス単位で行えるように設定する必要があります。
apps/
└ user-front-sp
└ user-front-pc
└ services
└ service_1 ← ここだけパイプラインを動かしたい!を実現させる。
└ service_2
└ service_3
....
└ admin
└ sadmin
libs/
└ components
└ user/
└ admin/
└ api-client
└ constants
また、各サービスごとに依存するLibなどにあったビルドを行う必要もあります。
そのため、各サービスではそのサービスのビルドプロセスをDockerfileで管理しマルチステージビルドを行うといったことも行わなければなりません。
これをすることにより、各サービスにあったビルドプロセスを適切に行えたり、各サービスに対応したDockerコンテナをdocker-composeで管理することで、開発者が開発に必要なサービスとそれと依存関係のあるサービスで必要なもののみをすばやく立ち上げることができます。
もちろん上記の考え方でテストやビルド成果物のデプロイもサービス単位で行えます。
マイクロサービスでの開発にはDockerをはじめとするコンテナ技術を基本的に使用しているため、コンテナ導入のための追加コストもかかりません。
最近のアーキテクチャとの関係
Monorepoはその出自にマイクロサービスが深く関わっていることは前章までの内容で理解していただけていると思いますが、
他のアーキテクチャにもMonorepoと似たことをしていてある意味では関連のあるものも存在します。
Modular Monolithとの違い
Monorepoに似た思想として、Modular Monolith(モジュラモノリス)というものも存在します。
Modular Monolithとは、分割を前提とし内部的には疎結合な構成となるモノリス
です。
リポジトリの構成としてはMonorepoと同様(ほぼ同じかな?)に一つのプロダクトを単一のリポジトリで管理し、その内部で各サービスが個々に管理されるような構成を取ります。
ここまで書くと一緒じゃね?となりますが、Modular Monolithはあくまで「分割を前提としたモノリス」なので、対応するパイプラインなども単一です。
要はソース管理とデプロイラインは単一で、内部的にはできるだけ疎結合
という点で、Monorepoと差があります。
Modular Monolithは、シード期などの開発の初期フェーズで「マイクロサービスのように疎結合なアプリにしたいが、今はプロダクトのローンチを一刻も早く行いたい!」といったようなスピード重視の時期に、将来を見据えて分割の準備をしておくような感じです。
なので、解決したいモチベーションは違くとも目指しているものは共通しておりModular MonolithはMonorepoと対になったりするものではなく、ただ段階が違うものと認識してもらうのがいいと思います。
※Modular Monolithに関してはShopifyの文献 がわかりやすいです。
まとめ
ここまでの話をまとめると以下のような感じになります。
- Monorepoはマイクロサービスでの開発における、コード管理の課題を解決するためのもの
- ただ単一リポジトリで管理するのではなく、パイプライン側や運用時のサービス単位での独立性は保ったままである必要がある。
- コンテナ技術を用いて、⇧を実現する。
- Modular Monolithとの違いはデプロイラインの状態で区別ができるが、そもそもの目指すべきものは同じでただ段階が違うだけ
おわりに
筆者はまだ業務でMonorepoを扱ったことがないのですが、マイクロサービスでの開発の中で資源の管理等で運用時に苦労したことや課題だと感じていたことへのアプローチがされているもので、「そうだよ、これだよ!」といった風に感じました。
また、⇧で書いたとおり実際に現場で使ったわけではないので、あまりデメリットらしいものが思いつかなかったですが、もしこの記事を読んでくださった方で「Monorepo現場で使ってるけどxxが難しい!」などお持ちの方いましたらぜひコメント等で教えてください!
ここまで読んでくださりありがとうございました。
最後にLGTMいただけると筆者の励みになりますので、心に余裕がある方はよろしくおねがいします!