Nx ワークスペースとモノレポ開発
Nxは、Angularを含む大規模アプリケーションのモノレポ管理に特化したビルドシステムです。Google、Microsoft、Cisco などの企業で採用されており、チーム間の境界管理、ビルドの高速化、コードの一貫性を実現します。
1. Nxとは何か
概要と哲学
Nxはスマートで高速なビルドシステムです。モノレポ内の複数プロジェクトを効率的に管理し、以下の課題を解決します。
- ビルド時間の短縮: キャッシュと影響範囲分析により、変更箇所に関連するタスクだけを実行
- コード共有の促進: ライブラリの作成・共有が容易で、コードの重複を防止
- 依存関係の管理: プロジェクト間の依存関係を自動検出し、ビルド順序を最適化
- 一貫性の確保: ジェネレータと境界ルールにより、チーム全体でコーディング規約を統一
Angular CLI との関係
NxはAngular CLIを拡張する形で動作します。ng コマンドの代わりに nx コマンドを使いますが、内部的にはAngular CLIの機能を利用しています。
# Angular CLI
ng serve my-app
ng build my-app
# Nx(同等のコマンド)
nx serve my-app
nx build my-app
Integrated Monorepo vs Package-Based Monorepo
| 方式 | 説明 | 適したケース |
|---|---|---|
| Integrated | Nxが全プロジェクトを一元管理。ビルド・テスト・リンクを統一的に実行 | 新規モノレポ、Angularプロジェクト |
| Package-Based | 各パッケージが独自の package.json を持つ。npm/yarn workspacesに近い | 既存プロジェクトの段階的移行 |
Turborepo との比較
| 機能 | Nx | Turborepo |
|---|---|---|
| キャッシュ | ローカル + リモート(Nx Cloud) | ローカル + リモート(Vercel) |
| コード生成 | ジェネレータ(強力) | なし |
| プロジェクトグラフ | 対話的なビジュアルグラフ | 基本的なグラフ |
| モジュール境界 | ESLintルールで強制可能 | なし |
| 主な対象 | Angular, React, Node.js 等 | 主にNext.js / Reactエコシステム |
2. Nx ワークスペースのセットアップ
ワークスペースの作成
npx create-nx-workspace@latest my-workspace
対話形式で以下を選択します:
- ワークスペースの種類: Angular(Integrated Monorepo)
- アプリケーション名: 最初に作成するアプリ名
- スタイル形式: CSS / SCSS / Less など
- Nx Cloud: リモートキャッシュの有効化(推奨)
ワークスペース構成
my-workspace/
├── apps/ # アプリケーション
│ └── my-app/
│ ├── src/
│ ├── project.json # プロジェクト固有の設定
│ └── tsconfig.json
├── libs/ # 共有ライブラリ
│ └── shared/
│ └── ui/
├── nx.json # Nx全体設定(キャッシュ、タスク設定)
├── tsconfig.base.json # ベースTypeScript設定
└── package.json
既存 Angular プロジェクトへの導入
# 既存のAngular CLIプロジェクトにNxを追加
npx nx init
nx init を実行すると、既存プロジェクトの構成を維持したまま、Nxのキャッシュ機能やタスク実行機能が追加されます。
3. プロジェクトとライブラリの作成
アプリケーションの作成
nx generate @nx/angular:application my-new-app
ライブラリの作成
nx generate @nx/angular:library my-lib
ライブラリの種類と命名規約
Nxのベストプラクティスでは、ライブラリを以下の種類に分類します。
| 種類 | ディレクトリ | 用途 | 例 |
|---|---|---|---|
| feature | libs/feature/ | スマートコンポーネント、ページ | feature-dashboard |
| ui | libs/ui/ | プレゼンテーショナルコンポーネント | ui-button, ui-card |
| data-access | libs/data-access/ | API通信、状態管理 | data-access-user |
| util | libs/util/ | ユーティリティ関数、ヘルパー | util-date, util-format |
| model | libs/model/ | インターフェース、型定義 | model-user, model-order |
# 例: ユーザー機能のライブラリを作成
nx generate @nx/angular:library feature-user-profile --directory=libs/feature
nx generate @nx/angular:library ui-avatar --directory=libs/ui
nx generate @nx/angular:library data-access-user --directory=libs/data-access
buildable vs publishable ライブラリ
| オプション | 説明 | ユースケース |
|---|---|---|
| buildable | 個別にビルド可能。キャッシュが効きやすい | モノレポ内部のみで使用 |
| publishable | npmパッケージとして公開可能 | 外部プロジェクトでも利用するライブラリ |
# publishableライブラリの作成
nx generate @nx/angular:library shared-ui --publishable --importPath=@my-org/shared-ui
4. プロジェクトグラフ(依存関係の可視化)
nx graph コマンド
# ブラウザでインタラクティブなグラフを表示
nx graph
プロジェクトグラフは、import文やTypeScriptのパスエイリアスから自動的に依存関係を検出します。
活用方法
- アーキテクチャの俯瞰: プロジェクト全体の構造を視覚的に把握
- 循環依存の検出: 循環参照がある場合にグラフ上で確認可能
- 影響範囲の確認: 特定のライブラリを変更した場合の影響先を表示
# 特定プロジェクトに関連するグラフのみ表示
nx graph --focus=my-app
# 影響を受けるプロジェクトのグラフを表示
nx affected:graph
5. affected コマンド(影響範囲の特定)
affected コマンドは、Gitの差分を解析し、変更の影響を受けるプロジェクトだけを対象にタスクを実行します。
# 影響を受けるプロジェクトのみビルド
nx affected -t build
# 影響を受けるプロジェクトのみテスト
nx affected -t test
# 影響を受けるプロジェクトのみリント
nx affected -t lint
CI/CD での活用
- name: Run affected tests
run: npx nx affected -t test --base=origin/main --head=HEAD
--base と --head で比較対象のコミットを指定することで、PRごとに必要最小限のビルド・テストだけを実行できます。これにより、大規模モノレポでもCI時間を大幅に短縮できます。
6. キャッシュとリモートキャッシュ
ローカルキャッシュ
Nxは同じ入力(ソースコード + 設定)に対するタスクの結果をローカルにキャッシュします。2回目以降は実行をスキップし、キャッシュから結果を返します。
# 初回: 通常実行(数秒〜数分)
nx build my-app
# 2回目: キャッシュヒット(数ミリ秒)
nx build my-app
# > nx run my-app:build [local cache]
Nx Cloud のリモートキャッシュ
チーム全員でキャッシュを共有するために、Nx Cloudでリモートキャッシュを利用できます。
# Nx Cloudに接続
npx nx connect-to-nx-cloud
リモートキャッシュの効果:
- チームメンバーAがビルドした結果を、メンバーBが再利用できる
- CIで実行した結果を、ローカル開発でも再利用できる
Distributed Task Execution (DTE)
Nx Cloudの分散タスク実行は、CIパイプラインでタスクを複数のマシンに自動分散します。
- name: Start CI run
run: npx nx-cloud start-ci-run --distribute-on="5 linux-medium-js"
- name: Run tasks
run: npx nx affected -t lint test build
キャッシュ対象の設定
{
"targetDefaults": {
"build": {
"cache": true,
"dependsOn": ["^build"]
},
"test": {
"cache": true
},
"lint": {
"cache": true
}
}
}
7. モジュール境界ルール(Module Boundaries)
@nx/enforce-module-boundaries
ESLintルールを使って、プロジェクト間の不正な依存関係を自動的に検出・禁止できます。これは大規模チームでのアーキテクチャ維持に非常に重要です。
tags によるプロジェクト分類
各プロジェクトの project.json にタグを設定します。
{
"tags": ["type:feature", "scope:user"]
}
{
"tags": ["type:ui", "scope:shared"]
}
depConstraints による依存ルール
{
"rules": {
"@nx/enforce-module-boundaries": [
"error",
{
"depConstraints": [
{
"sourceTag": "type:feature",
"onlyDependOnLibsWithTags": ["type:ui", "type:data-access", "type:util", "type:model"]
},
{
"sourceTag": "type:ui",
"onlyDependOnLibsWithTags": ["type:ui", "type:util", "type:model"]
},
{
"sourceTag": "type:data-access",
"onlyDependOnLibsWithTags": ["type:data-access", "type:util", "type:model"]
},
{
"sourceTag": "type:util",
"onlyDependOnLibsWithTags": ["type:util", "type:model"]
},
{
"sourceTag": "type:model",
"onlyDependOnLibsWithTags": ["type:model"]
}
]
}
]
}
}
この設定により以下のルールが強制されます:
- feature → ui, data-access, util, model に依存可能(他の feature には依存不可)
- ui → ui, util, model に依存可能(data-access や feature には依存不可)
- data-access → data-access, util, model に依存可能
- model → model のみに依存可能
React/Next.jsのエコシステムには、このようなモジュール境界ルールを強制する標準的な仕組みは存在しません。大規模プロジェクトでは eslint-plugin-import の制限的なルールや、独自のESLintルールを作成して対応します。
8. ジェネレータ(Generators)
Nx ジェネレータ vs Angular Schematics
| 機能 | Nx ジェネレータ | Angular Schematics |
|---|---|---|
| 位置づけ | Nxエコシステムの一部 | Angular CLIの一部 |
| 対象 | Angular、React、Node.js等マルチフレームワーク | Angularに特化 |
| テスト | Jest によるユニットテストが容易 | やや複雑 |
| ドライラン | --dry-run で事前確認可能 | 同様 |
カスタムジェネレータの作成
チーム固有のコード生成テンプレートをジェネレータとして定義できます。
# ジェネレータ用のライブラリを作成
nx generate @nx/plugin:plugin my-plugin
# カスタムジェネレータを追加
nx generate @nx/plugin:generator my-generator --project=my-plugin
カスタムジェネレータはチーム内のボイラープレートを統一し、新しいコンポーネントやサービスを一貫した構造で自動生成するために活用します。
9. マイクロフロントエンド with Nx
NxはModule Federationを使ったマイクロフロントエンドの構築をサポートしています。
ホストとリモートの作成
# ホストアプリケーション(シェル)の作成
nx generate @nx/angular:host shell --remotes=app1,app2
# リモートアプリケーションの追加
nx generate @nx/angular:remote app3 --host=shell
Module Federation の構成
import { ModuleFederationConfig } from '@nx/webpack';
const config: ModuleFederationConfig = {
name: 'shell',
remotes: ['app1', 'app2'],
};
export default config;
共有ライブラリの管理
- NxはModule Federation設定で共有ライブラリの重複を自動排除
libs/内のライブラリはシングルトンとして共有され、バンドルサイズを削減- 各リモートアプリは独立してビルド・デプロイ可能
10. CI/CD パイプラインとの統合
GitHub Actions での設定例
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- uses: nrwl/nx-set-shas@v4
- run: npx nx affected -t lint test build
ポイント
fetch-depth: 0で完全なGit履歴を取得(affected の差分計算に必要)nrwl/nx-set-shasでベースコミットを自動設定nx affectedで変更の影響を受けるプロジェクトだけを対象にタスク実行
11. Nx Console(VS Code 拡張機能)
Nx Console はVS Code拡張機能として提供されており、GUIでNxの機能を操作できます。
- ジェネレータの実行: フォーム形式でオプションを設定し、コンポーネントやライブラリを生成
- プロジェクトグラフ: VS Code上でインタラクティブに依存関係を表示
- タスクの実行: build / serve / test / lint をクリックで実行
- affected の確認: 現在の変更による影響範囲を視覚的に表示
詳細な活用方法は「VS Code環境構築のベストプラクティス」を参照してください。
12. 既存プロジェクトの移行
Angular CLI → Nx への段階的移行
ステップ1: Nxの追加
npx nx init
この段階では既存のプロジェクト構造はそのまま。Nxのキャッシュとタスク実行機能が追加されます。
ステップ2: ライブラリの分割
既存のモジュールを段階的にNxライブラリに分割します。
# 共有UIライブラリの作成
nx generate @nx/angular:library shared-ui --directory=libs/ui
# 既存コードをライブラリに移動
# → import パスを更新
ステップ3: 境界ルールの導入
ライブラリにタグを設定し、@nx/enforce-module-boundaries ルールを有効化して、アーキテクチャの一貫性を確保します。
移行のコツ
- 一度にすべてを移行しようとせず、段階的に進める
- まずキャッシュの恩恵を受けるだけでも大きな改善になる
- ライブラリの分割は、最も頻繁に変更されるモジュールから着手する
- CI/CDパイプラインでの
nx affectedの導入は早期に行うと効果的