目次を表示する

NestJS Deep Dive 2026 ─ 内部構造・再発明回避・パフォーマンスを 10 章で読み解く

おわりに ─ 4 原理回収 + 2026 構成パターン + v12 ロードマップ

第10章: おわりに ─ 4 原理回収 + 2026 構成パターン + v12 ロードマップ

4 原理マンダラ - DI / Decorator / 6 Layers / Memory

第0章「はじめに」で本シリーズは「この機能、自前で書いてるけど NestJS の何かと被ってないか?」という疑念から始まった。第1部で内部構造を、第2部で標準モジュール 18 種と再発明 TOP 10 を、第3部で性能・メモリ効率を、第4部で 15 のアンチパターンを見てきた。

表面的にはバラバラな話に見えただろうが、根は 4 つの原理しかなかった。本章で回収する。さらに 2026 構成パターンと v12 ロードマップで未来を見渡す。

原理1: DI コンテナを信頼する

各章での現出

  • 第1章DependenciesScanner → InstanceLoader の 2 段、Provider Scope(DEFAULT / REQUEST / TRANSIENT)、Custom Provider 4 種、forwardRef の解決メカニズム、ModuleRef
  • 第4章:Module DAG、Dynamic Module、v11 の Module 解決アルゴリズム刷新(オブジェクト参照ベース)
  • 第6章:再発明 TOP 10 の半数は「DI で取れるものを new していた」
  • 第7章:REQUEST 伝染と Durable Providers
  • 第9章:アンチパターン #4(forwardRef で隠蔽)/ #11(DI で取れるものを new)/ #15(main.ts 肥大化)

回収のフレーズ

「自分で new していないか」「DI で取れるものを取っているか」が出発点。NestJS の DI コンテナはあなたの想像より賢い。Object.create() で循環依存も解決し、Custom Provider 4 種で柔軟に依存を組める。DI を信頼するほど、テストが楽になり、リファクタリングが安全になる

原理2: Decorator は「メタデータ宣言 + 実行時 hook」の二層

各章での現出

  • 第2章(中核)Reflect.metadata の格納場所、__decorate ヘルパー、design:paramtypes、Stage 3 を採用しない理由、Custom Decorator 3 パターン、DiscoveryService
  • 第5章:18 モジュールの多くが Decorator + 起動時収集パターン(@OnEvent / @Cron / @CommandHandler
  • 第6章:Custom Decorator で再発明領域を埋める(@MurLock / @UseResilience

回収のフレーズ

@Injectable() の本質は「TypeScript に design:paramtypes を出させること」。これが分かれば、Decorator は怖くない。「特定 Decorator が付いた全 method を集める」自前フレームワーク的拡張DiscoveryService + MetadataScanner で書ける。NestJS が標準で提供している多くの「魔法」は、この仕組みで動いている。

原理3: リクエスト境界の「層」を正しく使い分ける

各章での現出

  • 第3章(中核):完全な順序(Middleware → Guard → Interceptor (before) → Pipe → Handler → Interceptor (after) → Filter)、各層の責務、global / controller / route スコープ、APP_GUARD token の重要性、ExecutionContext
  • 第9章:アンチパターン #5-9, #12-14 が層の責務違反

回収のフレーズ

「どこで何をすべきか」を間違えると、アンチパターン 15 のうち 8 個が生まれる。Middleware / Guard / Interceptor / Pipe / Handler / Filter はそれぞれ違う仕事をする。Interceptor で DB を引かない、Pipe で外部 API を叩かない、Service 内に try-catch を散らかさない ─ これらすべて、層の責務を正しく理解すれば自然に避けられる。

原理4: メモリ効率は DI スコープ × ストリーム × Logger 設計に集約

各章での現出

  • 第7章(中核):REQUEST 伝染と p99 レイテンシ低下、Durable Providers / nestjs-cls の使い分け
  • 第8章:class-validator vs typia 15,000 倍差、Pino + AsyncLocalStorage、Cache Stampede 対策
  • 第9章:アンチパターン #1-3, #10 が DI スコープに、#3 が Logger に集約

回収のフレーズ

アンチパターン 15 のうち 4 個は DI スコープ、1 個は Logger に直接関わる。表面的な「動いている」と本番運用での「速い」は別物。REQUEST 伝染を見抜き、Logger を Pino + ALS に切り替え、Cache に TTL Jitter を入れる ─ これだけで多くのプロダクションの p99 が改善する。

4 原理が章をまたいで効く ─ 全体マップ

graph TB
    subgraph 4 Principles
        P1[原理1<br/>DI コンテナを信頼]
        P2[原理2<br/>Decorator 二層構造]
        P3[原理3<br/>リクエスト 6 層]
        P4[原理4<br/>scope × stream × logger]
    end
    subgraph Part 1 / 内部構造
        C1[第1章 DI コンテナ]
        C2[第2章 Decorator]
        C3[第3章 リクエストライフサイクル]
        C4[第4章 Module + v11]
    end
    subgraph Part 2 / 棚卸し
        C5[第5章 18 モジュール]
        C6[第6章 再発明 TOP 10]
    end
    subgraph Part 3 / 性能
        C7[第7章 DI scope]
        C8[第8章 Validation/Logger/Cache]
    end
    subgraph Part 4 / 実践
        C9[第9章 15 アンチパターン]
    end
    P1 -.-> C1
    P1 -.-> C4
    P1 -.-> C6
    P1 -.-> C9
    P2 -.-> C2
    P2 -.-> C5
    P3 -.-> C3
    P3 -.-> C9
    P4 -.-> C7
    P4 -.-> C8
    P4 -.-> C9

同じ式が全章に効く」 ─ 第0章で予告した通り、4 原理が全章を貫いた。

2026 構成パターン ─ NestJS の現代的使い方

NestJS で書く側のロードマップとして、2026 年現在の代表的構成パターンを整理する。

1. Modular Monolith(モジュール式モノリス)

いきなり microservices にしない」が 2025-26 年の主流。NestJS の Module 境界がそのまま将来の microservice 化の seam(縫い目)になる。

apps/
├── api/                   ← 1 つの NestJS アプリ
│   └── src/
│       ├── auth/          ← 将来 microservice 化候補
│       ├── billing/       ← 将来 microservice 化候補
│       ├── notifications/
│       └── ...

規模が小さいうちは Module 境界だけ綺麗に保つ、規模が大きくなったら一部を @nestjs/microservices で別プロセスに切り出す。最初から microservices で始めるより運用コストが圧倒的に低い

2. CQRS / Saga(複雑ドメイン向け)

@nestjs/cqrs で Command / Query / Event を分離する DDD 系パターン。

// Command(書き込み)
class CreateUserCommand { /* ... */ }

@CommandHandler(CreateUserCommand)
class CreateUserHandler { /* mutate */ }

// Query(読み取り)
class GetUserQuery { /* ... */ }

@QueryHandler(GetUserQuery)
class GetUserHandler { /* read */ }

// Saga(複数 event の連鎖)
@Injectable()
class UserSagas {
  @Saga()
  signedUp = (events$) =>
    events$.pipe(
      ofType(UserCreatedEvent),
      map(() => new SendWelcomeEmailCommand()),
    );
}

注意:過剰適用しない。CRUD レベルまで Command/Query 分離すると保守コストが爆発する。ドメインが本当に複雑な領域にだけ。

3. LLM / Agent Backend

2025-26 年の急成長領域。NestJS は LLM / AI Agent のバックエンドとしても採用が増えている。

Server-Sent Events(SSE)で LLM ストリーミング

@Sse('chat/:id/stream')
streamChat(@Param('id') chatId: string): Observable<MessageEvent> {
  return new Observable(subscriber => {
    const abortController = new AbortController();

    this.llmService.streamCompletion(chatId, {
      signal: abortController.signal, // ← クライアント切断で課金停止
    }).subscribe({
      next: (chunk) => subscriber.next({ data: chunk }),
      complete: () => subscriber.complete(),
    });

    return () => abortController.abort(); // teardown
  });
}

重要

  • nginx の X-Accel-Buffering: no で proxy buffering を切る
  • AbortSignal でクライアント切断を LLM API に伝搬(課金停止
  • 長時間接続の管理(タイムアウト・再接続)

MCP Server

Model Context Protocol サーバを NestJS で書ける時代に。@nestjs-mcp/server@rekog/mcp-nest が登場。

@McpServer({ name: 'cats-api', version: '1.0.0' })
export class CatsMcpServer {
  @McpTool({ description: 'Find cats by breed' })
  async findCats(@McpInput() input: { breed: string }) {
    return await this.svc.findByBreed(input.breed);
  }
}

Decorator + Discovery の組み合わせで MCP tool を宣言できる ─ NestJS の哲学に合っている。

4. Graceful Shutdown × Kubernetes

K8s で動かすときの標準実装。

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { bufferLogs: true });
  app.useLogger(app.get(Logger));
  app.enableShutdownHooks(); // ← 必須
  await app.listen(3000);
}

@Injectable()
export class HealthService implements BeforeApplicationShutdown {
  private isShuttingDown = false;

  async beforeApplicationShutdown() {
    this.isShuttingDown = true;
    // K8s の readiness probe が 503 を返すように
    // → K8s は新規リクエストを送らなくなる
    // → in-flight は処理完了まで待つ
  }

  isReady() { return !this.isShuttingDown; }
}

// /health/readiness で参照
@Get('readiness')
readiness() {
  if (!this.healthService.isReady()) {
    throw new ServiceUnavailableException();
  }
  return this.health.check([/* ... */]);
}

K8s の terminationGracePeriodSeconds(デフォルト 30 秒)と整合させる。

5. PM2 / Cluster は K8s 環境では不要

歴史的に NestJS + PM2 cluster mode が標準だったが、K8s 環境では 1 pod = 1 process が現代的。K8s 自身が cluster の役割を果たす。

環境プロセス管理
旧オンプレ / VMPM2 cluster mode(CPU コア数だけ)
K8s1 pod = 1 process、HPA で水平スケール

v12 ロードマップ(参考、Q3 2026 予定)

NestJS v12 の方向(PR #16391 から推測される範囲):

項目内容
完全 ESM 移行CommonJS から ESM。Node.js の require(esm) 対応が決め手
Test:Jest → VitestVitest の速度・ESM 親和性
Lint:ESLint → oxlintRust 製で高速
Bundle:Webpack → Rspack同じく高速化
Standard Schema 対応@Body() / @Query() / @Param() で Zod / Valibot / ArkType を直接使える
その他NATS v3 対応、Express の graceful shutdown 強化、WebSocket disconnect reason

特に Standard Schema 対応は、第8章で扱った class-validator のパフォーマンス問題への正面回答になる可能性がある。

全章参考文献

NestJS 公式

GitHub(公式 + コミュニティ)

Trilon / NestJS チームブログ

深掘り技術記事

LLM / Agent / MCP

V8 / Node.js

最後に

NestJS は強い意見を持ったフレームワークだ。Spring 系 Java、ASP.NET Core、Angular 由来の DI とデコレータ、RxJS 思想 ─ これらを正しく理解しないと、表層だけで「動かす」ことはできても、本番で 5 倍遅くなるコードを書いてしまう。

しかし、4 つの原理を理解しさえすれば、機能はいくつ増えても本質は変わらない:

  1. DI コンテナを信頼する ─ 自分で new していないか、DI で取れるものを取っているか
  2. Decorator は二層構造 ─ メタデータ宣言 + 実行時 hook、Reflect.metadata で動く
  3. リクエスト境界の層を使い分ける ─ Middleware / Guard / Interceptor / Pipe / Handler / Filter の責務
  4. メモリ効率は scope × stream × logger ─ アンチパターン 15 のうち大半はここ

新しい機能(v11、v12、その先)が出るたびに「これは原理 1 の延長か、3 の延長か」と問えば、自然に位置付けられる。

最初の段階で第5章のチェックリストを月次でレビュー、第9章のアンチパターン 15 を四半期で点検、第6章の再発明 TOP 10 を新規開発のたびに参照 ─ この 3 つのリズムで、NestJS アプリケーションを成熟させていけるはずだ。

ここまで読んでいただき、ありがとうございました。本シリーズが、あなたの NestJS プロダクトを**「使い倒している」**状態へ引き上げる足場になれば幸いです。