NestJS Monorepo 構築:Nx/Yarn Workspaces で API・Lib を一元管理
大規模な NestJS プロジェクトを複数チームで開発する際、コードの重複や依存関係の管理に悩むことはありませんか?そんな課題を解決するのが Monorepo(モノレポ)構成です。本記事では、Nx と Yarn Workspaces を使って、API アプリケーションと共通ライブラリを一元管理する実践的な方法を解説します。初めて Monorepo に触れる方でも理解できるよう、段階的に構築手順を紹介していきますので、ぜひ最後までお読みください。
Monorepo とは何か
Monorepo(モノレポ)は、複数のプロジェクトやパッケージを単一のリポジトリで管理する開発手法です。
従来の方法では、API サーバー、管理画面、モバイルアプリなどをそれぞれ別のリポジトリで管理していました。しかし、この方法では共通コードの重複や、バージョン管理の複雑さが課題となります。
Monorepo を採用すると、以下のような構造で複数のアプリケーションとライブラリを一つのリポジトリにまとめられます。
mermaidflowchart TD
repo["Monorepo<br/>ルート"]
apps["apps/<br/>アプリケーション群"]
libs["libs/<br/>共通ライブラリ群"]
repo --> apps
repo --> libs
apps --> api["api/<br/>メイン API"]
apps --> admin["admin-api/<br/>管理者 API"]
apps --> batch["batch/<br/>バッチ処理"]
libs --> auth["auth/<br/>認証ライブラリ"]
libs --> db["database/<br/>DB アクセス"]
libs --> util["util/<br/>共通ユーティリティ"]
api -.->|使用| auth
api -.->|使用| db
admin -.->|使用| auth
admin -.->|使用| db
batch -.->|使用| util
図で理解できる要点
- apps フォルダには実行可能なアプリケーションを配置
- libs フォルダには共通ライブラリを配置
- 各アプリは必要なライブラリを参照して開発
Monorepo では、共通ロジックをライブラリとして切り出し、複数のアプリケーションから参照できます。これにより、コードの重複を減らし、変更の影響範囲を最小限に抑えられるのです。
Monorepo のメリット
Monorepo を採用すると、開発効率が大幅に向上します。
まず、コードの共有が容易になります。認証ロジックやデータベースアクセス層などを一度実装すれば、すべてのアプリケーションで再利用可能です。
また、リファクタリングの影響範囲を把握しやすくなります。共通ライブラリを変更した際、どのアプリケーションに影響があるかを即座に確認できるでしょう。
さらに、バージョン管理が統一されるため、依存パッケージの競合や互換性問題が減少します。
mermaidflowchart LR
change["ライブラリ変更"]
test["一括テスト実行"]
affected["影響範囲検出"]
deploy["安全なデプロイ"]
change --> test
test --> affected
affected --> deploy
style change fill:#e1f5ff
style deploy fill:#d4edda
図で理解できる要点
- 変更は一箇所で行い、影響範囲を自動検出
- CI/CD で一括テストが可能
- デプロイ前にすべての依存関係を検証
Monorepo 管理の課題
一方で、Monorepo には特有の課題も存在します。
最も大きな課題は、リポジトリのサイズ増大です。複数のプロジェクトが含まれるため、クローンやビルドに時間がかかる場合があります。
また、依存関係の管理が複雑化します。どのアプリケーションがどのライブラリを使用しているか、明確に把握する必要があるでしょう。
さらに、ビルドやテストの実行範囲を最適化しないと、CI/CD のパフォーマンスが低下します。変更のないコードまで毎回ビルドしてしまうのは非効率ですね。
| # | 課題 | 影響 | 対策 |
|---|---|---|---|
| 1 | リポジトリサイズ増大 | クローン・ビルド時間の増加 | 増分ビルド、キャッシュ活用 |
| 2 | 依存関係の複雑化 | 影響範囲の把握困難 | 依存関係グラフの可視化 |
| 3 | CI/CD のパフォーマンス | テスト・デプロイの遅延 | 変更検出による選択的実行 |
| 4 | チーム間のコード衝突 | マージコンフリクト増加 | 適切なディレクトリ分割 |
これらの課題を解決するために、Nx や Yarn Workspaces といった専門ツールが登場しました。
Nx と Yarn Workspaces による解決策
Nx と Yarn Workspaces を組み合わせることで、Monorepo の課題を効率的に解決できます。
Yarn Workspaces の役割
Yarn Workspaces は、パッケージ管理を一元化するツールです。
複数のパッケージの node_modules を統合し、ディスク容量を節約します。また、ローカルパッケージ間の依存関係を自動的に解決してくれるのです。
mermaidflowchart TB
root["ルート<br/>node_modules"]
subgraph workspace ["Yarn Workspaces"]
pkg1["apps/api<br/>package.json"]
pkg2["apps/admin<br/>package.json"]
pkg3["libs/auth<br/>package.json"]
end
pkg1 -.->|依存解決| root
pkg2 -.->|依存解決| root
pkg3 -.->|依存解決| root
pkg1 -->|参照| pkg3
pkg2 -->|参照| pkg3
図で理解できる要点
- すべてのパッケージが共通の node_modules を使用
- ローカルパッケージ間の依存を自動解決
- インストール時間とディスク容量を大幅削減
Nx の役割
Nx は、Monorepo 専用の開発ツールです。
変更されたコードだけをビルド・テストする「増分ビルド」機能を提供します。これにより、大規模プロジェクトでも高速な開発サイクルを維持できるでしょう。
また、依存関係グラフを自動生成し、視覚的に把握できます。コード生成機能も充実しており、新しいアプリやライブラリを素早く追加可能です。
| # | 機能 | 説明 | メリット |
|---|---|---|---|
| 1 | 増分ビルド | 変更部分のみビルド | ビルド時間 50-90% 削減 |
| 2 | 依存関係グラフ | 視覚的な依存関係表示 | 影響範囲の即座把握 |
| 3 | コード生成 | テンプレートからの自動生成 | 開発速度向上 |
| 4 | キャッシュ機能 | ローカル・リモートキャッシュ | 再ビルド時間ほぼゼロ |
| 5 | 並列実行 | タスクの並列処理 | CI/CD 時間短縮 |
Yarn Workspaces がパッケージ管理を担当し、Nx がビルド最適化と開発体験を向上させるという役割分担になります。
具体例:NestJS Monorepo の構築手順
それでは、実際に NestJS Monorepo を構築していきましょう。ここでは、メイン API、管理者 API、認証ライブラリを含む構成を作成します。
ステップ 1:Nx Workspace の初期化
まず、Nx を使って新しい Workspace を作成します。
bash# Nx の最新版をグローバルインストール
yarn global add nx
# NestJS プリセットで Workspace を作成
npx create-nx-workspace@latest my-nestjs-monorepo \
--preset=nest \
--packageManager=yarn
コマンドを実行すると、いくつかの質問が表示されます。
- Application name:
api(最初のアプリケーション名) - Use Nx Cloud:
No(後で設定可能)
これで基本的な Workspace が作成され、最初の NestJS アプリケーション api が生成されます。
ステップ 2:Yarn Workspaces の設定
次に、Yarn Workspaces を有効化します。
ルートの package.json を確認しましょう。
json{
"name": "my-nestjs-monorepo",
"version": "1.0.0",
"private": true,
"workspaces": ["apps/*", "libs/*"]
}
コードの説明
private: trueにより、このパッケージ自体を npm に公開しないよう設定workspaces配列で、アプリケーションとライブラリのディレクトリを指定- ワイルドカード
*により、配下のすべてのパッケージを自動認識
これで、apps と libs 配下のすべてのパッケージが Yarn Workspaces の管理対象になります。
ステップ 3:共通ライブラリの作成
認証機能を共通ライブラリとして作成します。
bash# 認証ライブラリを生成
nx generate @nx/nest:library auth \
--directory=libs/auth \
--buildable \
--publishable=false
コマンドオプションの説明
@nx/nest:library: NestJS 用ライブラリ生成ジェネレータ--directory: ライブラリの配置ディレクトリ--buildable: 独立してビルド可能にする設定--publishable=false: npm への公開を無効化
これで libs/auth ディレクトリが作成され、基本的な NestJS モジュール構造が生成されます。
mermaidflowchart LR
cmd["nx generate<br/>コマンド"]
files["ファイル生成"]
config["設定更新"]
ready["開発準備完了"]
cmd --> files
files --> config
config --> ready
files -.->|作成| module["auth.module.ts"]
files -.->|作成| service["auth.service.ts"]
files -.->|作成| spec["テストファイル"]
config -.->|更新| tsconfig["tsconfig.json"]
config -.->|更新| nx_json["nx.json"]
ステップ 4:認証ライブラリの実装
認証サービスを実装していきます。
まず、JWT 認証に必要なパッケージをインストールします。
bash# 認証関連パッケージのインストール
yarn add @nestjs/jwt @nestjs/passport passport passport-jwt
yarn add -D @types/passport-jwt
次に、認証サービスを実装します。
typescript// libs/auth/src/lib/auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
/**
* 認証処理を担当するサービス
* JWT トークンの生成と検証を行います
*/
@Injectable()
export class AuthService {
constructor(private readonly jwtService: JwtService) {}
/**
* ユーザー情報から JWT トークンを生成
* @param userId ユーザー ID
* @param email メールアドレス
* @returns JWT トークン文字列
*/
generateToken(userId: string, email: string): string {
const payload = { sub: userId, email };
return this.jwtService.sign(payload);
}
}
実装のポイント
@Injectable()デコレータで DI コンテナに登録JwtServiceを注入して JWT 操作を委譲- ペイロードには最小限の情報のみを含める
続いて、認証モジュールを設定します。
typescript// libs/auth/src/lib/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
/**
* 認証機能を提供するモジュール
* 他のアプリケーションから import して使用します
*/
@Module({
imports: [
JwtModule.register({
secret: process.env.JWT_SECRET || 'default-secret',
signOptions: {
expiresIn: '1h', // トークンの有効期限
},
}),
],
providers: [AuthService],
exports: [AuthService], // 他のモジュールから使用可能にする
})
export class AuthModule {}
モジュール設定の説明
JwtModule.register()で JWT の基本設定を行うexportsにAuthServiceを指定して外部公開- 環境変数でシークレットキーを管理(本番環境では必須)
最後に、ライブラリのエントリポイントを設定します。
typescript// libs/auth/src/index.ts
/**
* 認証ライブラリの公開インターフェース
* 他のパッケージからは、このファイルを通じてインポートします
*/
export * from './lib/auth.module';
export * from './lib/auth.service';
これで、@my-nestjs-monorepo/auth という名前で他のアプリケーションから参照できるようになります。
ステップ 5:メイン API での認証ライブラリ使用
既存の api アプリケーションで認証ライブラリを使用します。
まず、apps/api/src/app/app.module.ts を編集します。
typescript// apps/api/src/app/app.module.ts
import { Module } from '@nestjs/common';
import { AuthModule } from '@my-nestjs-monorepo/auth'; // 共通ライブラリをインポート
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [
AuthModule, // 認証モジュールを登録
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
インポートのポイント
- ライブラリ名は
@{workspace名}/{ライブラリ名}の形式 - 相対パスではなくパッケージ名でインポート
- TypeScript の path mapping により自動解決
次に、コントローラーで認証機能を使用します。
typescript// apps/api/src/app/app.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from '@my-nestjs-monorepo/auth';
/**
* 認証エンドポイントを提供するコントローラー
*/
@Controller('auth')
export class AppController {
constructor(private readonly authService: AuthService) {}
/**
* ログインエンドポイント
* ユーザー情報を受け取り、JWT トークンを返却します
*/
@Post('login')
login(
@Body() loginDto: { userId: string; email: string }
) {
const token = this.authService.generateToken(
loginDto.userId,
loginDto.email
);
return {
success: true,
token,
message: '認証に成功しました',
};
}
}
エンドポイント実装の説明
AuthServiceを DI で注入POST /auth/loginでトークン生成- 実際の環境では、データベースでの認証処理を追加
ステップ 6:管理者 API アプリケーションの追加
別の API アプリケーションを追加します。
bash# 管理者用 API アプリケーションを生成
nx generate @nx/nest:application admin-api
生成された apps/admin-api でも同じ認証ライブラリを使用できます。
typescript// apps/admin-api/src/app/app.module.ts
import { Module } from '@nestjs/common';
import { AuthModule } from '@my-nestjs-monorepo/auth'; // 同じライブラリを使用
@Module({
imports: [AuthModule],
// ... 管理者 API 固有の設定
})
export class AppModule {}
このように、共通ライブラリを複数のアプリケーションで再利用できます。
mermaidflowchart TB
subgraph libs ["libs/ 共有ライブラリ"]
auth["@my-nestjs-monorepo/auth<br/>認証ライブラリ"]
end
subgraph apps ["apps/ アプリケーション"]
api["api<br/>メインAPI"]
admin["admin-api<br/>管理者API"]
end
auth -->|import| api
auth -->|import| admin
api -->|提供| endpoint1["POST /auth/login"]
admin -->|提供| endpoint2["POST /admin/auth/login"]
style auth fill:#ffe6cc
style api fill:#cce5ff
style admin fill:#cce5ff
図で理解できる要点
- 一つの認証ライブラリを複数の API で共有
- 各 API は独立してデプロイ可能
- ライブラリの変更は全 API に自動反映
ステップ 7:依存関係グラフの確認
Nx の依存関係グラフを確認しましょう。
bash# 依存関係グラフを可視化
nx graph
このコマンドを実行すると、ブラウザが開き、アプリケーションとライブラリの依存関係が視覚的に表示されます。
どのアプリがどのライブラリに依存しているか、一目で把握できるでしょう。
ステップ 8:ビルドとテストの実行
個別のアプリケーションをビルドします。
bash# api アプリケーションのビルド
nx build api
# admin-api アプリケーションのビルド
nx build admin-api
# auth ライブラリのビルド
nx build auth
Nx は依存関係を自動的に解決し、必要なライブラリを先にビルドします。
変更されたコードのみをビルドする場合は以下のコマンドを使用します。
bash# 変更されたプロジェクトのみビルド
nx affected:build
# 変更されたプロジェクトのみテスト
nx affected:test
affected コマンドの動作
- Git の差分から変更されたファイルを検出
- 影響を受けるプロジェクトのみ処理
- CI/CD で大幅な時間短縮を実現
ステップ 9:開発サーバーの起動
複数のアプリケーションを並行して開発できます。
bash# api を起動(ポート 3000)
nx serve api
# 別のターミナルで admin-api を起動(ポート 3001)
nx serve admin-api --port=3001
どちらのアプリケーションも、共通の認証ライブラリの変更をホットリロードで即座に反映します。
ステップ 10:TypeScript パス設定の確認
tsconfig.base.json を確認します。
json{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@my-nestjs-monorepo/auth": ["libs/auth/src/index.ts"]
}
}
}
パス設定の役割
- エイリアスを使ったインポートを可能にする
- IDE の補完機能を有効化
- リファクタリング時のパス自動更新
Nx が自動的にこの設定を管理してくれるため、手動での編集は基本的に不要です。
実践的な運用パターン
Monorepo を実際のプロジェクトで運用する際のベストプラクティスを紹介します。
ライブラリの分割戦略
ライブラリは機能ごとに適切に分割しましょう。
| # | ライブラリ名 | 責務 | 依存関係 |
|---|---|---|---|
| 1 | @proj/auth | 認証・認可 | JWT, Passport |
| 2 | @proj/database | DB アクセス | TypeORM, Prisma |
| 3 | @proj/common | 共通型定義 | なし |
| 4 | @proj/config | 設定管理 | dotenv |
| 5 | @proj/logger | ロギング | Winston, Pino |
分割の基準
- 単一責任の原則に従う
- 再利用性が高い機能を優先
- 循環依存を避ける構造にする
CI/CD での最適化
GitHub Actions での設定例を示します。
yaml# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Nx affected のために全履歴を取得
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'yarn'
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build affected projects
run: yarn nx affected:build --base=origin/main
- name: Test affected projects
run: yarn nx affected:test --base=origin/main --coverage
- name: Lint affected projects
run: yarn nx affected:lint --base=origin/main
CI/CD 最適化のポイント
fetch-depth: 0で全履歴を取得し、affected 検出を正確にする--frozen-lockfileでロックファイルの整合性を保証affectedコマンドで変更部分のみ処理し、時間を短縮
環境変数の管理
各アプリケーションで共通の環境変数を管理します。
ルートに .env ファイルを配置します。
bash# .env
# 共通設定
NODE_ENV=development
LOG_LEVEL=debug
# データベース設定
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
# 認証設定
JWT_SECRET=your-super-secret-key
JWT_EXPIRES_IN=1h
# API 固有設定
API_PORT=3000
ADMIN_API_PORT=3001
各アプリケーションで環境変数を読み込みます。
typescript// libs/config/src/lib/config.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService as NestConfigService } from '@nestjs/config';
/**
* 環境変数を型安全に取得するサービス
*/
@Injectable()
export class ConfigService {
constructor(private configService: NestConfigService) {}
/**
* JWT シークレットキーを取得
*/
get jwtSecret(): string {
return (
this.configService.get<string>('JWT_SECRET') || ''
);
}
/**
* JWT 有効期限を取得
*/
get jwtExpiresIn(): string {
return (
this.configService.get<string>('JWT_EXPIRES_IN') ||
'1h'
);
}
/**
* データベース URL を取得
*/
get databaseUrl(): string {
return (
this.configService.get<string>('DATABASE_URL') || ''
);
}
}
環境変数管理のベストプラクティス
.envはルートに配置し、全アプリで共有.env.exampleでテンプレートを提供.envは.gitignoreに追加し、コミットしない- 型安全な Config Service で取得
デプロイ戦略
アプリケーションごとに個別デプロイが可能です。
Docker を使用したデプロイ例を示します。
dockerfile# apps/api/Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
# 依存関係をインストール
COPY package.json yarn.lock ./
COPY tsconfig.base.json ./
RUN yarn install --frozen-lockfile
# ソースコードをコピー
COPY . .
# api アプリケーションとその依存ライブラリをビルド
RUN yarn nx build api --prod
# ランタイムイメージ
FROM node:18-alpine
WORKDIR /app
# ビルド成果物をコピー
COPY --from=builder /app/dist/apps/api ./
COPY --from=builder /app/node_modules ./node_modules
# 環境変数の設定
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD ["node", "main.js"]
Docker ビルドの最適化
- マルチステージビルドで最終イメージサイズを削減
- 依存ライブラリを含めてビルド
- 本番環境用の最適化オプションを使用
Kubernetes でのデプロイ設定例です。
yaml# k8s/api-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
labels:
app: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: my-registry/api:latest
ports:
- containerPort: 3000
env:
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: app-secrets
key: jwt-secret
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
resources:
requests:
memory: '256Mi'
cpu: '250m'
limits:
memory: '512Mi'
cpu: '500m'
---
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
selector:
app: api
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
Kubernetes デプロイのポイント
- 複数レプリカで冗長性を確保
- Secret で機密情報を管理
- Resource limits で安定稼働を実現
パフォーマンスモニタリング
Nx のキャッシュ機能を活用します。
json// nx.json
{
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": ["build", "test", "lint"],
"cacheDirectory": "node_modules/.cache/nx"
}
}
}
}
キャッシュの効果
- 同一コードの再ビルドをスキップ
- ローカル開発で劇的な高速化
- CI/CD でもキャッシュを活用可能
ビルド時間の比較例を示します。
| # | 状況 | 初回ビルド | キャッシュ利用 | 短縮率 |
|---|---|---|---|---|
| 1 | 全プロジェクト | 180 秒 | 5 秒 | 97% |
| 2 | affected のみ | 45 秒 | 3 秒 | 93% |
| 3 | 単一アプリ | 30 秒 | 2 秒 | 93% |
まとめ
NestJS Monorepo の構築方法について、Nx と Yarn Workspaces を使った実践的な手順を解説しました。
Monorepo を採用することで、コードの重複を減らし、依存関係を明確に管理できます。特に大規模プロジェクトや複数チームでの開発において、その効果は顕著でしょう。
Yarn Workspaces がパッケージ管理を統一し、Nx が増分ビルドや依存関係グラフで開発体験を向上させます。この組み合わせにより、開発速度と品質の両立が可能になるのです。
まずは小さなプロジェクトから始めて、徐々に Monorepo の恩恵を実感してください。認証やロギングといった共通機能をライブラリ化するだけでも、大きな効果が得られます。
本記事で紹介した構成をベースに、チームの要件に合わせてカスタマイズしてみてください。Monorepo による効率的な開発を、ぜひ体験してみましょう。
関連リンク
articleNestJS Monorepo 構築:Nx/Yarn Workspaces で API・Lib を一元管理
articleNestJS × TypeORM vs Prisma vs Drizzle:DX・性能・移行性の総合ベンチ
articleNestJS メモリリーク診断:Node.js Profiler と Heap Snapshot で原因を掴む
articleNestJS 2025 年の全体像:Express・Fastify・Serverless の使い分け早わかり
articleNestJS 監視運用:SLI/SLO とダッシュボード設計(Prometheus/Grafana/Loki)
articleNestJS クリーンアーキテクチャ:UseCase/Domain/Adapter を疎結合に保つ設計術
articleVite プラグインフック対応表:Rollup → Vite マッピング早見表
articleNestJS Monorepo 構築:Nx/Yarn Workspaces で API・Lib を一元管理
articleTypeScript Project References 入門:大規模 Monorepo で高速ビルドを実現する設定手順
articleMySQL Router セットアップ完全版:アプリからの透過フェイルオーバーを実現
articletRPC アーキテクチャ設計:BFF とドメイン分割で肥大化を防ぐルータ戦略
articleMotion(旧 Framer Motion)× TypeScript:Variant 型と Props 推論を強化する設定レシピ
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来