0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Node.js】リファクタリングを通して学ぶドメイン駆動設計 Part7: 依存性注入の設定

Last updated at Posted at 2025-12-07

はじめに

この記事は「リファクタリングを通して学ぶドメイン駆動設計」シリーズのPart7(最終回)です。

前回はコントローラー(Controller) の実装について説明しました。今回は依存性注入(Dependency Injection) について解説し、これまで作成してきた各層を組み立てて、実際に動作するアプリケーションを完成させます。

開発環境

開発環境は以下の通りです。

  • Windows11
  • Docker Engine 27.0.3
  • Docker Compose 2
  • PostgreSQL 18.1
  • Node.js 24.11.0
  • npm 11.6.2
  • TypeScript 5.9.3
  • Express 5.1.0
  • Prisma 6.18.0
  • Zod 4.1.12

依存性注入の設定

依存性注入(Dependency Injection: DI) とは、クラスが必要とする他のクラスを外部から渡す手法です。

エントリーポイントで各層のインスタンスを生成し、依存関係を解決します。

// main.ts
import express from 'express';
import { PrismaClient } from '@prisma/client';

const app = express();
const prisma = new PrismaClient();
const PORT = process.env.PORT || 3000;

app.use(express.json());

// 依存性注入の設定
const userRepository = new PrismaUserRepository(prisma);
const createUserUseCase = new CreateUserUseCase(userRepository);
const getUserUseCase = new GetUserUseCase(userRepository);
const getAllUsersUseCase = new GetAllUsersUseCase(userRepository);
const updateUserUseCase = new UpdateUserUseCase(userRepository);
const deleteUserUseCase = new DeleteUserUseCase(userRepository);

const userController = new UserController(
  createUserUseCase,
  getUserUseCase,
  getAllUsersUseCase,
  updateUserUseCase,
  deleteUserUseCase
);

// ルーティング
app.get('/', (req, res) => {
  res.json({ message: 'Server is running' });
});

app.post('/users', (req, res) => userController.create(req, res));
app.get('/users', (req, res) => userController.getAll(req, res));
app.get('/users/:id', (req, res) => userController.getById(req, res));
app.put('/users/:id', (req, res) => userController.update(req, res));
app.delete('/users/:id', (req, res) => userController.delete(req, res));

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

process.on('SIGINT', async () => {
  await prisma.$disconnect();
  process.exit(0);
});

依存関係の流れ

依存性注入により、以下のような依存関係が構築されます。

main.ts
  ├─ PrismaClient (Prisma)
  │   └─ PrismaUserRepository (インフラ層)
  │       ├─ CreateUserUseCase (アプリケーション層)
  │       ├─ GetUserUseCase (アプリケーション層)
  │       ├─ GetAllUsersUseCase (アプリケーション層)
  │       ├─ UpdateUserUseCase (アプリケーション層)
  │       └─ DeleteUserUseCase (アプリケーション層)
  │           └─ UserController (プレゼンテーション層)
  │               └─ setupUserRoutes (ルーティング)
  └─ Express App

依存性注入のポイント

ポイント1: 依存関係の一方向性

依存関係は常に外側から内側へ向かいます。

プレゼンテーション層
    ↓ 依存
アプリケーション層
    ↓ 依存
ドメイン層
    ↓ 依存(インターフェース経由)
インフラ層

重要な原則:

  • ドメイン層は他の層に依存しない
  • アプリケーション層はドメイン層にのみ依存
  • インフラ層はドメイン層のインターフェースを実装
  • プレゼンテーション層はアプリケーション層に依存

ポイント2: インスタンス生成の順序

依存性注入では、インスタンスを依存される側から生成します。

// 1. 最も依存されるもの(インフラ層)
const prisma = new PrismaClient();
const userRepository = new PrismaUserRepository(prisma);

// 2. 中間層(アプリケーション層)
const createUserUseCase = new CreateUserUseCase(userRepository);
const getUserUseCase = new GetUserUseCase(userRepository);
// ...

// 3. 最も依存するもの(プレゼンテーション層)
const userController = new UserController(
  createUserUseCase,
  getUserUseCase,
  getAllUsersUseCase,
  updateUserUseCase,
  deleteUserUseCase
);

ポイント3: テスト時の差し替え

依存性注入により、テスト時に実装を差し替えられます。

// 本番環境
const userRepository = new PrismaUserRepository(prisma);
const useCase = new CreateUserUseCase(userRepository);

// テスト環境
const mockRepository: IUserRepository = {
  save: jest.fn(),
  findById: jest.fn(),
  findAll: jest.fn(),
  update: jest.fn(),
  delete: jest.fn(),
  existsByEmail: jest.fn(),
};
const useCase = new CreateUserUseCase(mockRepository);

ポイント4: 変更への柔軟性

データベースを変更する場合、リポジトリの実装を差し替えるだけです。

// Prismaを使用
const userRepository = new PrismaUserRepository(prisma);

// TypeORMに変更
const userRepository = new TypeORMUserRepository(dataSource);

// ユースケース以降は変更不要
const createUserUseCase = new CreateUserUseCase(userRepository);
const userController = new UserController(createUserUseCase, ...);

ディレクトリ構成

完成したアプリケーションのディレクトリ構成は以下のようになります。

src/
├── domain/                        # ドメイン層
│   ├── entities/
│   │   └── User.ts
│   ├── value-objects/
│   │   ├── Email.ts
│   │   ├── UserName.ts
│   │   └── UserId.ts
│   └── repositories/
│       └── IUserRepository.ts
├── application/                   # アプリケーション層
│   └── usecases/
│       ├── CreateUserUseCase.ts
│       ├── GetUserUseCase.ts
│       ├── GetAllUsersUseCase.ts
│       ├── UpdateUserUseCase.ts
│       └── DeleteUserUseCase.ts
├── infrastructure/                # インフラ層
│   └── prisma/
│       └── PrismaUserRepository.ts
├── presentation/                  # プレゼンテーション層
│   ├── controllers/
│   │   └── UserController.ts
│   └── routes/
│       └── userRoutes.ts
├── middlewares/
│   └── validate.middleware.ts
├── schemas/
│   └── user.schema.ts
└── index.ts                       # エントリーポイント

まとめ

全7回にわたり、DDDを使ったリファクタリングについて解説してきました。

各回の内容

  • Part1: 値オブジェクトの作成
  • Part2: エンティティの作成
  • Part3: リポジトリインターフェースの定義
  • Part4: ユースケースの作成
  • Part5: リポジトリの実装
  • Part6: コントローラーの実装
  • Part7: 依存性注入の設定(今回)

DDDの重要な概念

概念 説明 配置される層
値オブジェクト 値そのものを表すオブジェクト ドメイン層
エンティティ 一意の識別子を持つオブジェクト ドメイン層
リポジトリ データ永続化の抽象化 ドメイン層(インターフェース)
インフラ層(実装)
ユースケース アプリケーションの具体的な操作 アプリケーション層
コントローラー HTTPリクエスト/レスポンスの処理 プレゼンテーション層
依存性注入 インスタンスを外部から渡す エントリーポイント

DDDのメリットとデメリット

DDDを適用することで以下のメリットがあります。

  • ビジネスルールが明確になる
    • 値オブジェクトとエンティティでドメイン知識を表現できる
    • 「ユーザーとは何か」「メールアドレスとは何か」が明確になる
  • 保守性が向上する
    • 各層が独立しているため、変更の影響範囲が限定的
    • データベースを変更してもドメイン層は影響を受けない
  • テストしやすくなる
    • リポジトリをモックに差し替えやすい設計
    • ユースケース単位でテストできる
  • チーム開発がしやすい
    • 責務が明確なので、並行作業がしやすくなる

一方、以下のようなデメリットがあります。

  • コード量が増える
    • 層を分離することで、ファイル数とコード量が増える
  • 学習コストがかかる
    • DDDの概念(エンティティ、値オブジェクト、リポジトリなど)を理解する必要がある

そのため、以下の場合は過剰設計になる可能性があります。

  • シンプルなCRUDアプリケーション
  • 短期間で使い捨てのプロトタイプ
  • 個人の小規模プロジェクト

そのため、今回のようなシンプルなCRUDアプリケーションでは過剰設計ですが、DDDの各概念を理解する良い練習になったと思います。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?