フロントエンド・バックエンド分離からモノレポへ:AI駆動開発時代の最適解
はじめに
最近、教材共有プラットフォームの開発プロジェクトで、フロントエンド(FE)とバックエンド(BE)を別々のディレクトリで管理していた構成を、**モノレポ(Monorepo)**に統合する作業を行いました。
この記事では、なぜ統合したのか、どのように実施したのか、そしてAI駆動開発時代において、この選択がなぜ重要なのかを詳しく解説します。
プロジェクトの初期状態
分離構成の問題点
project-root/
├── backend/ # Expressサーバー
│ ├── package.json # 227MBのnode_modules
│ └── src/
└── frontend/ # Reactアプリ
├── package.json # 541MBのnode_modules
└── src/
合計768MBものnode_modulesが存在し、以下の問題がありました:
- 型定義の重複 -
material.tsのような共通型が両方に存在 - 依存関係の不整合 - バージョンの違いによる予期せぬバグ
- 開発効率の低下 - 2つのターミナルで別々にサーバーを起動
- 環境変数の分散 - .envファイルが複数存在
モノレポとマルチレポの比較
モノレポ(Monorepo)とは
モノレポは、複数の関連プロジェクトを1つのGitリポジトリで管理する手法です。Google、Facebook、Microsoftなどの大企業が採用しています。
monorepo/
├── packages/
│ ├── backend/
│ ├── frontend/
│ └── shared/ # 共通コード
├── package.json # ルート設定
└── pnpm-workspace.yaml
マルチレポ(Multirepo)とは
マルチレポは、各プロジェクトを独立したGitリポジトリで管理する従来の手法です。
github.com/
├── project-backend/
└── project-frontend/
メリット・デメリット比較表
モノレポとマルチレポの特徴比較
型定義の共有
- モノレポ:✅ 一元管理で整合性保証
- マルチレポ:❌ 手動でコピーが必要
依存関係管理
- モノレポ:✅ pnpmで効率的に共有
- マルチレポ:❌ 重複インストール
開発体験
- モノレポ:✅ 1コマンドで全体起動
- マルチレポ:❌ 複数ターミナル必要
CI/CD
- モノレポ:✅ 統合テストが簡単
- マルチレポ:❌ 連携が複雑
コードレビュー
- モノレポ:✅ 全体の変更を把握
- マルチレポ:❌ 別々にレビュー
リポジトリサイズ
- モノレポ:❌ 大きくなりやすい
- マルチレポ:✅ 個別に管理
アクセス制御
- モノレポ:❌ 全体への権限必要
- マルチレポ:✅ 細かく制御可能
初期セットアップ
- モノレポ:❌ やや複雑
- マルチレポ:✅ シンプル
AI駆動開発時代におけるモノレポの重要性
1. AIが理解しやすいコンテキスト
AI(Claude、GitHub Copilot等)にとって、関連コードが1つのリポジトリにあることは非常に重要です。
// packages/shared/types/material.ts
export interface Material {
id: string;
title: string;
uploadedBy: string;
version?: string;
}
// AIは両側のコードを同時に理解できる
// packages/backend/src/api/materials.ts
import { Material } from '@shared/types';
// packages/frontend/src/components/MaterialCard.tsx
import { Material } from '@shared/types';
2. AIによる自動リファクタリング
型定義を変更した際、AIが影響範囲を正確に把握して修正提案できます:
// AIへの指示:「Material型にdownloadCountフィールドを追加して、関連箇所を全て更新して」
// → AIはbackend/frontend両方の変更箇所を一度に提案可能
3. 統合テストの自動生成
// AIが生成するE2Eテスト例
describe('教材アップロード', () => {
it('FEからBEまでの一連の流れをテスト', async () => {
// AIは全体の流れを理解してテストを生成
const response = await uploadMaterial(testData);
expect(response.status).toBe(200);
const materials = await getMaterials();
expect(materials).toContainEqual(testData);
});
});
実装手順
1. 秘匿情報の保護(最重要)
# .envファイルのバックアップ
mkdir -p ~/secure-backup
cp backend/.env ~/secure-backup/backend.env.backup
cp frontend/.env ~/secure-backup/frontend.env.backup
# .env.exampleの作成(秘匿情報を除去)
cat > .env.example << EOF
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
# API Keys
OPENAI_API_KEY=your-api-key-here
# 注意:実際の値は.envファイルに記載してください
EOF
2. pnpmワークスペースの設定
pnpmは、ハードリンクを使用して依存関係を効率的に管理するパッケージマネージャーです。
# pnpm-workspace.yaml
packages:
- 'packages/*'
3. ディレクトリ構造の再編成
# packagesディレクトリを作成
mkdir -p packages
# 既存ディレクトリを移動
mv backend packages/
mv frontend packages/
# 共有パッケージを作成
mkdir -p packages/shared/src/types
4. 統合package.jsonの作成
{
"name": "education-platform",
"private": true,
"scripts": {
"dev": "pnpm run --parallel dev",
"build": "pnpm run --recursive build",
"test": "pnpm run --recursive test"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.0.0"
}
}
5. 共通型定義の統一
// packages/shared/src/types/index.ts
export interface User {
id: string;
email: string;
role: 'teacher' | 'student';
}
export interface Material {
id: string;
title: string;
uploadedBy: User['id'];
downloads: number;
likes: number;
version?: {
parent?: string;
description?: string;
};
}
遭遇した問題と解決方法
問題1: CI/CDの失敗
# エラー:pnpm-lock.yamlが見つからない
Error: Unable to find pnpm-lock.yaml
原因:.gitignoreにpnpm-lock.yamlが含まれていた
解決:
# .gitignoreから削除
sed -i '' '/pnpm-lock.yaml/d' .gitignore
# lockファイルをコミット
git add pnpm-lock.yaml
git commit -m "fix: Add pnpm-lock.yaml for CI"
問題2: 環境変数の誤検知
# セキュリティスキャンの誤検知
Warning: Potential secret found in process.env.DATABASE_URL
解決:環境変数の参照は問題ないことを確認し、.gitleaks.tomlで除外設定を追加
問題3: TypeScriptパスの解決
// Cannot find module '@shared/types'
解決:tsconfig.jsonにパスマッピングを追加
{
"compilerOptions": {
"paths": {
"@shared/*": ["../shared/src/*"]
}
}
}
セキュリティ上の注意事項
1. 環境変数の管理
# ❌ 絶対にやってはいけないこと
git add .env
git commit -m "Add environment variables"
# ✅ 正しい方法
echo ".env" >> .gitignore
git add .gitignore
2. Git履歴のクリーニング
もし誤って秘匿情報をコミットした場合:
# BFG Repo-Cleanerを使用
bfg --delete-files .env
git reflog expire --expire=now --all
git gc --prune=now --aggressive
3. プライベートリポジトリの設定
# GitHubでプライベートリポジトリに変更
gh repo edit --visibility private
パフォーマンスの改善結果
Before(マルチレポ)
- node_modules合計: 768MB
- インストール時間: 3分45秒
- 起動コマンド: 2つのターミナル
After(モノレポ with pnpm)
- node_modules合計: 412MB(46%削減)
- インストール時間: 1分20秒(64%短縮)
- 起動コマンド: 1コマンド
今後の展望
1. Turborepoの導入
ビルドキャッシュと並列実行でさらなる高速化:
{
"turbo": {
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
}
2. AIペアプログラミングの活用
- GitHub Copilot Workspace:モノレポ全体を理解した提案
- Claude Projects:プロジェクト全体のコンテキストを保持
- Cursor IDE:AIファーストの開発環境
3. マイクロフロントエンドへの拡張
packages/
├── backend/
├── frontend-teacher/ # 教員用UI
├── frontend-student/ # 生徒用UI
├── frontend-admin/ # 管理者用UI
└── shared/
まとめ
モノレポへの移行は、単なる技術的な選択ではなく、AI駆動開発時代における戦略的な判断です。
主なメリット
- 開発効率の向上 - 型の一元管理とホットリロード
- AIとの相性 - コンテキストの統一で精度向上
- 保守性の改善 - リファクタリングの安全性
注意点
- 秘匿情報の管理 - 環境変数は必ず.gitignoreに
- 初期設定の複雑さ - 適切なツール選択が重要
- チーム教育 - 新しいワークフローの習得
AI駆動開発が当たり前になる未来において、モノレポはAIと人間が協調して開発する最適な環境を提供します。プロジェクトの規模や特性に応じて、適切な構成を選択することが重要です。