TurboRepoによるモノレポ構成とCI/CDの実践
Posted date at 2025-04-12
要件定義を進めているプロジェクトについて、TurboRepoを使用したモノレポ構成を使用した開発環境構築を行いました。また、併せてGithubActionsを使用したCloudRunへのCI/CDを構築しましたので紹介します。
「この構成は非常に実践的でモダンなWebアプリケーション開発のベストプラクティスを体現していると言えます」
🚀はじめに
モダンなWebアプリケーション開発において、フロントエンドとバックエンドの連携、型安全性の確保、開発効率の向上など、さまざまな課題に直面します。私の場合は、フロントエンドとバックエンドの型安全の確保(共有)がこれまでの課題でこれを解消したいと思いました。今回、Next.js、Prisma、Honoを組み合わせたTurboRepoモノレポ構成を使用して、型安全なフルスタックアプリケーションの開発環境を構築しました。
🚀実践したこと
以下の構成をプロジェクトで用意しました。
1 フロントエンド(Next.js App Router)
・TypeScript + Next.js 15
・Clerk認証
・Tailwind CSS
・Vitest + React Testing Library
2 バックエンド(Hono)
・TypeScript + Hono
・Prismaとの統合
・Vitest
3 データベース(Prisma)
・マイグレーション管理
・型生成
4.CI/CD(Husky+GitHubActions)
・Google Cloud Runデプロイ
・テスト自動化
・コミット前の自動チェック(pre-commit)
・ESLintによるコード品質チェック
・Prettierによるコードフォーマット
・プッシュ前の安全性確保(pre-push)
・ビルドチェック
・テストの実行
🐡ディレクトリ構成
2025.4.12現在モノレポ構成ができた時点での構成です。
hubtan/ ├── apps/ │ ├── client/ # フロントエンド(Next.js) │ │ ├── src/ │ │ │ ├── app/ # App Router │ │ │ │ ├── layout.tsx # ルートレイアウト │ │ │ │ └── page.tsx # ホームページ │ │ │ ├── components/ # 共通コンポーネント │ │ │ │ ├── ui/ # UIコンポーネント │ │ │ │ ├── features/ # 機能コンポーネント │ │ │ │ └── vitest/ # テスト用コンポーネント │ │ │ │ ├── Counter.tsx │ │ │ │ └── __tests__/ # テストファイル │ │ │ │ └── Counter.test.tsx │ │ │ └── test/ # テスト設定・ユーティリティ │ │ │ ├── setup.ts # テストセットアップ │ │ │ └── test-utils.tsx # テストユーティリティ │ │ ├── public/ # 静的ファイル │ │ │ ├── manifest.json # PWA設定 │ │ │ └── icons/ # アプリアイコン │ │ ├── vitest.config.ts # Vitestの設定 │ │ ├── tailwind.config.js # Tailwindの設定 │ │ ├── postcss.config.js # PostCSSの設定 │ │ ├── next.config.js # Next.jsの設定 │ │ └── package.json # クライアント依存関係 │ │ │ └── server/ # バックエンド(Hono) │ ├── src/ │ │ ├── routes/ # APIルート │ │ ├── middlewares/ # ミドルウェア │ │ ├── utils/ # ユーティリティ │ │ └── __tests__/ # バックエンドテスト │ ├── vitest.config.ts # Vitestの設定 │ └── package.json # サーバー依存関係 │ ├── packages/ │ ├── prisma/ # Prisma関連 │ │ ├── schema.prisma # データベーススキーマ │ │ ├── migrations/ # マイグレーションファイル │ │ ├── src/ │ │ │ ├── client.ts # Prismaクライアント │ │ │ └── __tests__/ # Prismaテスト │ │ └── package.json │ │ │ └── types/ # 共有型定義 │ ├── src/ │ │ ├── user.ts # ユーザー関連の型 │ │ ├── api.ts # API関連の型 │ │ └── __tests__/ # 型のテスト │ └── package.json │ ├── .github/ │ └── workflows/ # GitHub Actions │ ├── frontend.yml # フロントエンドCI/CD │ ├── backend.yml # バックエンドCI/CD │ └── test.yml # テスト専用ワークフロー │ ├── .husky/ # Gitフック設定 │ ├── pre-commit # コミット前の処理 │ ├── commit-msg # コミットメッセージ検証 │ └── pre-push # プッシュ前の処理 │ ├── .eslintrc.json # ESLint設定 ├── .prettierrc # Prettier設定 ├── .commitlintrc.json # Commitlint設定 ├── package.json # ルート package.json ├── pnpm-workspace.yaml # pnpmワークスペース設定 └── turbo.json # Turborepo設定
🚀モノレポとは
モノレポ(Monorepo)は、複数のプロジェクトやパッケージを単一のリポジトリで管理する開発手法です。
初めてのモノレポ導入という状況において、実績と安定性を重視してTurboRepoを選択しましたが、JStackやBunWorkSpaceというモノレポツールもあります。今回モノレポを構成するにあたって以下のエンジニアの方の知識をお借りしました。非常に分かり易く丁寧に解説されていますので私と同じような課題を抱えている方は是非ご覧になってください。
🐡BunWorkSpaceについて解説されている動画
TypeScriptでフルスタックエンジニアになる(@arafipro)さんのYoutube動画
🐡JStackについて解説されている動画
プログラミングスクールJISOUの渡邉 臣 | JISOU(@Sicut_study)さんのYoutube動画
🚀モノレポのメリットと今回実現したこと
1 型安全性の確保
・フロントエンドとバックエンド間の型の共有
・APIレスポンスの型保証
・データベーススキーマと型の一貫性
WorkSpaceに定義した型をフロントエンドとバックエンドから参照できるようにしました。以下はPrismaで仮に型生成したユーザ型(User)と、TypeScriptで用意した所属型(DepartmentType)をフロントエンドとバックエンドから参照した例です。
// apps/server/src/routes/user.ts import { User } from "@hubtan/prisma"; app.get('/users/:id', async (c) => { const user: User = await prisma.user.findUnique({ where: { id: c.params.id } }); return c.json(user); }); // apps/client/src/app/test/page.tsx import { SignOutButton } from "@clerk/nextjs"; import { ColorTest } from "apps/client/src/components/test/color/ColorTest"; import React from "react"; import { db, User } from "@hubtan/prisma"; import { DepartmentType } from "@hubtan/types"; export default async function TestPage() { const users = await db.user.findMany(); return ( <div> <ColorTest /> <SignOutButton /> </div> ); }
2 開発効率の向上
・共通コードの再利用
・ビルドプロセスの最適化
turbo.jsonに定義を用意し、プロジェクト全体のビルドやテストを行えるようにしました。
{ // Turboレポのスキーマを指定 "$schema": "https://turborepo.org/schema.json", // プロジェクト全体で共有される依存関係を指定 // .envファイルの変更があった場合、全タスクが再実行される "globalDependencies": [".env"], // 実行可能なタスクの定義 "tasks": { // ビルドタスク "build": { // 依存するパッケージのビルドを先に実行(^はワークスペース依存を示す) "dependsOn": ["^build"], // ビルド成果物の出力先を指定(キャッシュの対象) "outputs": [".next/**", "dist/**"] }, // Prismaの型生成タスク // スキーマ変更時に必ず実行する必要があるためキャッシュ無効 "db:generate": { "cache": false }, // DBスキーマの同期タスク // データベースの状態を変更するためキャッシュ無効 "db:push": { "cache": false }, // 開発サーバー起動タスク "dev": { // 開発サーバーはキャッシュ不要 "cache": false, // サーバーを常時起動状態に保つ "persistent": true }, // 本番環境起動タスク "start": { // ビルドが完了してから実行 "dependsOn": ["build"], // 毎回新しい状態で起動するためキャッシュ無効 "cache": false }, // リントタスク "lint": { // リントは出力ファイルを生成しない "outputs": [] }, // クリーンアップタスク // ビルド成果物やキャッシュを削除 "clean": { "cache": false } } }
🚀CI/CDの構築
🐡 実現したこと
1 自動化されたデプロイフロー(全体)
・mainブランチへのプッシュで自動デプロイ
・テスト→ビルド→デプロイの一貫した実行
・手動デプロイによるヒューマンエラーの防止
2 品質管理の自動化(HuskyとLintStagedを使用)
・コードチェック(ESLint)とコード整形(Prettier)
・プッシュ前の自動テスト実行
・ビルドエラーの早期発見
・テストカバレッジの維持
3 Google Cloud Platformとの統合(GithubActionsを使用)
・Artifact Registryへの自動プッシュ
・Cloud Runへの自動デプロイ
🐡 CI/CDに関係するフォルダ構成
. ├── .github/ │ └── workflows/ │ ├── frontend.yml # フロントエンドのデプロイワークフロー │ ├── backend.yml # バックエンドのデプロイワークフロー │ └── test.yml # テスト実行ワークフロー ├── apps/ │ ├── client/ # フロントエンドアプリケーション │ │ ├── Dockerfile # CloudRun環境構築 │ │ ├── vitest.config.ts # テスト設定 │ │ └── package.json │ └── server/ # バックエンドアプリケーション │ ├── Dockerfile # CloudRun環境構築 │ ├── vitest.config.ts # テスト設定 │ └── package.json ├── packages/ │ ├── prisma/ # データベーススキーマ │ │ └── schema.prisma │ └── types/ # 共有型定義 ├── .husky/ # Git hooks設定 │ ├── pre-commit # コミット前の品質チェック │ ├── commit-msg # コミットメッセージ検証 │ └── pre-push # プッシュ前のビルド・テスト ├── turbo.json # Turboレポ設定(ビルド・テストパイプライン) └── pnpm-workspace.yaml # pnpmワークスペース設定
🐡 実行コマンド
1 プロジェクト全体
# 依存関係のインストール pnpm install # 全体のビルド pnpm build # 全体の開発サーバー起動 pnpm dev # 全体のテスト実行 pnpm test
2 フロントエンドのみ(client)
# フロントエンドのビルド pnpm --filter "@hubtan/client" build # フロントエンドの開発サーバー起動 pnpm --filter "@hubtan/client" dev # フロントエンドのテスト実行 pnpm --filter "@hubtan/client" test:run
3 バックエンドのみ(server)
# バックエンドのビルド pnpm --filter "@hubtan/server" build # バックエンドの開発サーバー起動 pnpm --filter "@hubtan/server" dev # バックエンドのテスト実行 pnpm --filter "@hubtan/server" test
🐡 ワークフローの実行順序
1 品質管理のステップ(Husky)
1 コミット前のチェック(pre-commit)
・ESLintによるコード品質チェック
・Prettierによるコードフォーマット
2 プッシュ前の総合チェック(pre-push)
・ビルドの実行確認
・テストの実行
2 GitHubActionsでのデプロイステップ
GitHubActionsを使用したデプロイは以下の順で実行しています。
sequenceDiagram participant Developer participant GitHub participant Actions participant GCP Developer->>GitHub: git push GitHub->>Actions: トリガー検知 Actions->>Actions: パス変更チェック alt フロントエンド変更 Actions->>Actions: frontend.yml実行(Github上でのテスト+ビルド) Actions->>GCP: フロントエンドデプロイ(GCP上でのビルド+デプロイ) end alt バックエンド変更 Actions->>Actions: backend.yml実行(Github上でのテスト+ビルド) Actions->>GCP: バックエンドデプロイ(GCP上でのビルド+デプロイ) end
🐡 実行結果
🚀まとめ
今回、モノレポ構成とCI/CDを実現しました。実際の過程では多くのエラーが発生し、それも踏まえて記事にしたかったのですが、半分泣きそうになりながらの精神状態であったため、やってみた(やった)の内容となりました(本当はこの手順を実施すれば誰でもできるようなものにしたかった)。
ただ、100歩譲って実際にどのようなことができるかの紹介にはなったと思います。
また、@しょうたまでDMいただければこの時点のソースコードも提供できますのでそのような方が見えましたらご連絡ください。
←ホームに戻る