はじめに
はじめまして。
株式会社メタップスホールディングス 26卒内定者エンジニアの Tomu です。
現在は実務で Vue や Ruby を扱っていますが、
個人開発ではNext.js × Express × Turborepo × pnpm を用いたDockerレスなモノレポ環境を構築し、7ヶ月ほど運用しています。
この記事では、実際に運用してきた中で得られた実践的な運用Tipsをまとめました。
同じ構成で開発している方、これからモノレポを作りたい方の参考になれば幸いです。
※ 型・バリデーション共有についても載せているので、読んでいただけると嬉しいです!
👉 tRPCを使わずに「型+バリデーション」を共有するTSモノレポ構成(Next.js×Express)
目次
なぜモノレポにしたのか
理由は 「型とバリデーションを共有したかったから」 です。
具体的には、 frontend / backend / shared の3レイヤーを1つのリポジトリで効率よく管理したかったから。
実際に、shared で型やバリデーション、utilityを共有することで、開発体験を大きく改善できました。
技術・ディレクトリ構成
技術構成
主要技術:Next.js / Express / turborepo / Prisma / NeonDB / Zod / pnpm
CICD:GithubActions / Biome / Vercel / Render
ライブラリ:LIFF / LINE Webhook / OpenAIAPI / Stripe
全体的には「TSフルスタック」といった感じです。
テストは未実装ですが学習も含めて近々実装したいです。
※ OpenAIAPIとStripeは過去に実装まで行いましたが、要件変更により現在は機能から外しています。
ディレクトリ構成
root/ # .git(ルートでgit運用)
├── .github/ # CI / build・format(Biome)
├── apps/
│ ├── frontend/ # Next.js(LIFFアプリ)
│ │ └── src/
│ │ └── features/ # APIドメイン別・カスタムフック
│ │ ├── auth/
│ │ └── users/
│ └── backend/ # Express(API) / LINE webhook
│ └── src/
│ └── features/ # APIドメイン別・controllers/services
│ ├── auth/
│ └── users/
├── shared/ # Zodスキーマ / 共通型 / Utils
│ └── src/
│ ├── features/ # APIドメイン別・zodスキーマ & 型
│ │ ├── auth/
│ │ └── users/
│ └── utils/
│
├── pnpm-workspace.yaml
├── turbo.json
└── package.json # ルート共通設定
ディレクトリ設計の考え方
1. モノレポは「おもちゃ箱」
モノレポは “複数のプロジェクトを1つのリポジトリで管理する方法” と説明されますが、
自分の中では 「おもちゃ箱」 のようなイメージで捉えています。
おもちゃをただ箱に入れるだけだと必要なパーツを探すのが大変ですが、
「車」「ぬいぐるみ」「積み木」などで仕切られていれば、目的のものをすぐ取り出せます。
モノレポも同じで、frontend / backend / shared の3レイヤーを分けて、
さらに auth / users / stores などの“コンテキスト単位”で仕切ることで、
「どこに何を書くか」で迷う時間がかなり減り、認知負荷の低い構成になったと感じています。
2. featuresディレクトリで「ドメインごと」にそろえる
このモノレポでは、apps/frontend・apps/backend・shared の3レイヤーそれぞれで
同じ「ドメイン単位」のディレクトリを持つようにしています。
features/authfeatures/shiftsfeatures/users
たとえば auth に関するコードは、
- frontend:カスタムフック(
apps/frontend/src/features/auth) - backend:認証 API やドメインロジック(
apps/backend/src/features/auth) - shared:型・バリデーション(
shared/src/features/auth)
というように、レイヤーは違っても “同じfeaturesに集まる” ように統一しています。
これにより「このAPIのフロント側どこ?」「この型どこ?」が一瞬で分かるようになり、開発スピードとDXが大きく改善します。
実際の開発・運用フロー
実際の開発・運用の流れは以下のような感じで、個人開発感が強いです。
Dockerを使わず、pnpmとTurborepo、PaaS(Vercel / Render)で軽量・爆速開発しているイメージです。
1. ローカル編集(frontend / backend / shared)
| - pnpmで依存管理(workspace)
| - turbo run dev で frontend / backend ローカルサーバー並列起動
| - turbo run biome / type-check / build で並列実行・キャッシュで高速化
|
| /// GitHub に push
|
2. Actions で lint / format(biome) ・ build を並列実行
|
3. Vercel / Render で自動デプロイ
|
4. GitHub Projectでissue管理
モノレポ変更直後はいろんな運用課題がありましたが、現在は安定して開発・運用を回せています。
実際に、モノレポ移行前・直後より体感2〜3倍ほど開発速度がはやくなり、運用管理も安定化できています。
運用Tips
1. Git運用とIssue管理の工夫
私のプロジェクトでは、.gitをルートディレクトリに置いてリポジトリ管理をしています。
そのため、GitHub上にはルート配下にあるfrontend / backend / sharedの3つのワークスペースに対する変更が履歴に載ります。
ルートでGit運用する場合の課題
ルートディレクトリにgitを置くということは、
一つのリポジトリ内に、frontend / backend / sharedの3つのワークスペースの作業履歴が混じることとになります。
そのため、どのコミット履歴がどのワークスペースに対して作業を行なったか追うのが面倒です。
そこで、GithubProjectを用いてissue管理を行うことにしました。
GitHub Projectでissue管理
コミット履歴と作業内容の認識を一致させたいと思い、Github Projectでモノレポ環境のissue管理を行っています。
ラベル活用でワークスペースの作業内容を可視化
ラベルを活用して、「Frontend」「Backend」「Shared」といったようにカスタマイズしているので、「どのディレクトリのissueなのか」の確認が容易になりました。
また、以前はNotionで管理していて、issue管理とPR管理が完全に分離していたので、その点においてもスイッチングコストが減って、結果的に開発スピードが速くなりました。
しかしながら、チーム開発においてはルートでgit運用するかどうかは慎重に判断した方が良いと思います。
2. pnpmで依存管理を最適化
pnpm導入前の悩み
pnpm導入前は、frontend / backend / sharedの三つの依存をそれぞれのpackage.jsonで管理していましたが、依存の把握や管理が雑になっていました。
また、npmだとnode_modulesが重く、生成や削除に時間がかかっていました。
そこで、パッケージマネージャをnpmからpnpmに変更・導入して依存管理の最適化を行いました。
pnpm導入の利点
pnpmのおかげで、以下のような利点を得られました。
- ルートで依存をまとめて管理できる
- node_modulesが軽くなる
- npm / yarnよりも速い
- 依存漏れ防止(npmよりも依存解決が厳しい)
プロジェクト全体として、npmを使ってた頃より依存管理がクリーンになりました。
依存一覧 pnpm run dep:list
- workspaceごとに依存を一覧表示・確認
依存バージョン確認 pnpm run dep:ver
- リストテーブル形式ですべての依存バージョンを表示・確認
おや、Deperecatedが出ているので修正しないといけないですね。
3. Turborepoで開発速度を最大化
TurborepoはVercelが公式に提供しているmonorepo環境に特化した高速ビルドツールです。
Turborepo導入前の悩み
モノレポ前・移行直後は、frontendとbackendのリポジトリごとに、ビルドやlint / format、型チェックを行っていていましたが、悩みがありました。
- リポジトリを跨いでコマンドを打つ煩わしさ
- 直列実行しかできない
- 毎回ソースコード全体を見て実行する(キャッシュなしの不利点)
Turborepo導入後、こうした悩みは全て解決されました。
並列実行・キャッシュで高速化
Turbo の 並列実行 + キャッシュ により、開発効率が大幅に向上しました。
| 処理 | 通常時 | フルキャッシュ時 | 実行処理 |
|---|---|---|---|
| dev 起動 | 約 800ms | - | frontend + backend サーバー並列起動 |
| build | 10秒 | 6秒 | 並列実行 + キャッシュ |
| biome | 3秒 | 0.1秒 | 並列実行 + キャッシュ |
| type-check | 3秒 | 0.1秒 | 並列実行 + キャッシュ |
ビルド時のターミナル
- 通常時:10秒
- フルキャッシュ時:6秒
4. TurborepoでCIを高速化
CI でも Turborepo を使うことで、複数ワークスペースのチェックを
並列実行で効率化 しています。(※ GitHub Actions ではキャッシュ未使用)
| チェック内容 | GitHub Actions全体 | 単体の処理(Turborepo × Biome) |
|---|---|---|
| lint / format | 40〜50 秒 | 十数秒 |
| build | 1分〜1分30秒 | 約50秒 |
lint / format 時の実行ログ
- GitHub Actions全体:40秒
- biome単体の処理:12秒
5. Vercel×Renderで自動デプロイ
本プロジェクトでは、フロントは Vercel、バックは Render を使い、リポジトリ単位で自動デプロイしています。
PaaS を使うことで Docker を使わずとも
・push → build → deploy の自動化
・個人開発でも安定稼働
が実現できています。
(本記事の主題ではないため詳細は割愛します)
最後に
個人開発でも、pnpm や Turborepo、PaaS を組み合わせることで、軽量なモノレポ環境が比較的簡単に構築できます。また、ディレクトリ設計や技術を工夫すれば安定して運用できるんだなと実感しています。
今回まとめた内容が、これからモノレポを試してみたい方や 同じ構成で開発している方の参考になれば嬉しいです。
ここまで読んでいただき、ありがとうございました!






