turborepo を通して知る、Monorepo 管理のメリット・デメリット

    turborepo を通して知る、Monorepo 管理のメリット・デメリット

    目次

      記事の目的

      Monorepo(MonoRepository)管理についての知見を共有します。
      案件、個人開発ともに Monorepo 管理を試しているので、その際に気づいたメリット、デメリットをお話しします。
      Webを探すと「Monorepo」「モノレポ」「one-repo」「uni-repo」等の表記がありますが、本記事では Monorepo で統一します。

      Monorepo(モノレポ)とは

      複数の関連するコード群を管理している1つのリポジトリのことです。
      「Mono」の単語が「単一の」という意味なので、「Monorepo」は1つにまとめられたリポジトリという意味です。
      例を出すと、1つのアプリケーションにおける、サーバサイドのコードとクライアントサイドのコードを1つのリポジトリで管理する感じです。

      逆に、関連するコード群ごとにリポジトリを分ける方法は「Polyrepo」と呼ばれています。
      (他には「many-repo」「multi-repo」等)

      turborepoを利用したMonorepo管理を実際に行ってみてどうだったか?

      個人的にも、仕事でも turborepo を利用して、Monorepo を作成しました。
      turborepo は Vercel 社(Next.jsを開発している会社)が開発している Monorepo ツールです。

      TypeScript 環境だと Nx という Monorepoツールがあり、調べたところそちらも人気でした。
      個人で Monorepo を管理するときには Nx を使っても良さそうだったのですが、 Next.js を使う予定だったので、相性が良さそうな turborepo を選択しました。
      どちらのライブラリもDocumentはキレイでしたので、Nxでも問題はないはずです。

      注意点として、turborepo は npm/yarn のワークスペースをラップしているので、
      パッケージ管理についてるワークスペースについても概念を理解しておく必要があります。

      以降に実際どこが便利になって、どこが辛かったのかを記述しています。
      ただし、筆者は前述の通り TypeScript と turborepo を利用した Monorepo 環境を触ったため、以降は TypeScript/turborepo 前提でお話しします。

       

      使ってみてわかったメリット

      1. ライブラリのバージョンを一元管理

      TypeScript開発だと、現状 npm もしくは yarn, pnpm 等のパッケージマネージャでライブラリ管理をされていると思うので、リポジトリ内でのバージョン管理はできます。

      しかしプロジェクト(ここで指すプロジェクトは1リポジトリとして扱うシステムを指す)が複数個組み合わさっているシステムで構成されている場合での、バージョン管理は難しいです。

      Monorepo の運用で複数のプロジェクトを運用した場合、1つのリポジトリに共用のソースコードが含まれることになるので、管理が楽になります。
      turborepoでは、ライブラリが格納されている node_modules がリポジトリのルートに保存され、それぞれのプロジェクトがルートの node_modules を参照することになります。

      2. ソースコード資産の共有

      フロントエンドとバックエンドの両方でTypeScriptを利用する場合だと、 型情報の共有や、外部への通信処理の共通化等が実施できます。

      バックエンドのみで利用している場合でも、Logger を共通で書いたりできて便利です。

      3. 利用するツールの共通化

      TSConfig、ESLint 設定も共有できるので、プロジェクト作成時毎に設定ファイルを再定義する必要がありません。
      また、全体でインストールするツールの設定も turborepo ではできます。
      そのため Prettier や ESLint を新規プロジェクトでも利用可能な状態にしておけます。

       

      使ってみてわかったデメリット

      1. エディターの親和性が低い

      基本的に VS Code 等のエディターは、開かれているフォルダが Polyrepo である前提で開かれています。
      そのため、Linter や Formatter、Jest等の Test ツール拡張機能等がそのままでは正常に動かない可能性が非常に高いです。
      たとえば VS Codeの拡張機能だと、デフォルトでrootディレクトリにあるconfigファイルを見に行くので、configファイルが見つからなくなります。

      対策としては、共通の設定を見に行くように設定をしたり、configファイルで親の設定を見るようにしたりと、結構泥臭くて面倒くさい作業が必要になります。

      2. 細々な設定が必要な場合につらい

      共通で書いた ESLint の設定とフレームワーク側で用意されている ESLint の設定とで差異がある場合には注意が必要です。
      差異があると、フレームワーク側の書き方をした箇所で warning や error 等が発生します。

      その場合、個別のプロジェクトごとにESLint の設定をオーバーライドする必要があります。

      3. ビルド設定、CI/CD設定がつらい

      こちらは Monorepo を利用しているので当然といえば当然ですが、そのままビルドすると全プロジェクトのソースコードがパックされてしまうので、非常にヘビーな成果物ができ上がります。
      Docker等のコンテナを利用してデプロイされている方だと、クソデカ Docker Image が爆誕します。

      上記の問題を防ぐために、turborepo では --scope オプションと --docker オプションの2コマンドがあります。
      --scope は、対象がどのプロジェクトかを指定するコマンドで、--docker はDocker用にプロジェクトを切り出すためのコマンドです。

      しかし現状だと、上記コマンドをうてば楽に Docker Image が生成されるわけではなく、結局 Dockerfile 内で少しゴニョゴニョする必要があります。
      公式に参考用 Dockerfile が記載されているので、そこは親切です)

      4. 管理ツールの不具合に翻弄される

      Monorepo をそのまま動かすのは厳しいので、基本 Monorepo 管理用ツールを利用することになりますが、管理用ツールで不具合が出た場合、ビルド処理が止まってしまったりします。

      実際、 turborepo 利用時にバージョンを上げたところ、Dockerfile 内で turborepo が動いてくれない問題に出くわしました。
      これはMonorepo管理ツールに限った話ではありませんが、メインのビルド環境のバージョン管理や不具合にも対応する必要が発生します。

      結論:Monorepoはどのようなときに利用するのが良いか

      一見良さそうに見える Monorepo ですが、ビルドや設定周りが現状だと結構泥臭いと感じました。
      確かに共通で色々とできるのは便利ですが、 Polyrepo でも Git の Submodule を利用した方法等があるので、本当に必要かを一度洗い直した上で導入することをオススメします。

      ある程度設定や技術に詳しい人がいたり、 TypeScript環境をフロントエンドとバックエンドで利用している状況だったり、 Monorepo 内のプロジェクト間で同じ設定を使いまわしても良い状況であればマッチすると思います。

      とくに設定周りは大変なので、詳しい人がいないと爆死する可能性がある点には注意です。

      まとめ

      Polyrepo や Monorepo に限らず、ものごとには良い面と悪い面があります。

      試しに触ってみるのは良いと思いますが、実際に運用されるプロダクトで利用される場合は、 ご自身のプロダクトとマッチしているかを考えた上で採用するのがベターだと思いました。

      参考資料