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?

NestJSの設計原則

Posted at

はじめに

本記事では、NestJSアプリケーションを設計・実装する際の主要な原則について解説します。

モジュラーアーキテクチャ

NestJSの核となる設計思想は、アプリケーションを機能的な単位(モジュール)に分割することです。

@Module({
  imports: [
    TypeOrmModule.forFeature([User]),
    AuthModule,
    ConfigModule,
  ],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

各モジュールは:

  • 単一の責任を持つ
  • 独立してテスト可能
  • 再利用可能な単位として機能

依存性注入(DI)

DIはNestJSアプリケーションの設計の中心です:

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly usersRepository: Repository<User>,
    private readonly configService: ConfigService,
  ) {}
}

DIの利点:

  • 疎結合な設計
  • テスタビリティの向上
  • コードの再利用性

レイヤードアーキテクチャ

アプリケーションは明確に分離されたレイヤーで構成:

src/
├── controllers/    # プレゼンテーション層
├── services/       # ビジネスロジック層
├── repositories/   # データアクセス層
└── domain/        # ドメインモデル

実装例:

// Controller層(プレゼンテーション)
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }
}

// Service層(ビジネスロジック)
@Injectable()
export class UsersService {
  constructor(private readonly usersRepository: UsersRepository) {}

  async create(data: CreateUserDto): Promise<User> {
    // ビジネスロジックの実装
    return this.usersRepository.create(data);
  }
}

// Repository層(データアクセス)
@Injectable()
export class UsersRepository {
  constructor(
    @InjectRepository(User)
    private readonly repository: Repository<User>,
  ) {}

  async create(data: CreateUserDto): Promise<User> {
    return this.repository.save(data);
  }
}

型安全性とバリデーション

DTOとバリデーションデコレータを使用した堅牢な型システム:

export class CreateUserDto {
  @IsString()
  @IsNotEmpty()
  readonly name: string;

  @IsEmail()
  readonly email: string;

  @IsString()
  @MinLength(8)
  readonly password: string;
}

エラー処理

集中的なエラー処理メカニズム:

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const status = exception.getStatus();

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      message: exception.message,
    });
  }
}

ベストプラクティス

1. 設定管理

@Injectable()
export class AppConfigService {
  constructor(private configService: ConfigService) {}

  get databaseUrl(): string {
    return this.configService.get<string>('DATABASE_URL');
  }
}

2. トランザクション管理

@Injectable()
export class UsersService {
  constructor(private dataSource: DataSource) {}

  async createUserWithProfile(userData: CreateUserDto) {
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      // トランザクション内の処理
      await queryRunner.commitTransaction();
    } catch (err) {
      await queryRunner.rollbackTransaction();
      throw err;
    } finally {
      await queryRunner.release();
    }
  }
}

3. ガード(認証・認可)

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  constructor(private reflector: Reflector) {
    super();
  }

  canActivate(context: ExecutionContext) {
    const isPublic = this.reflector.get<boolean>(
      'isPublic',
      context.getHandler()
    );

    if (isPublic) {
      return true;
    }

    return super.canActivate(context);
  }
}

実践的な注意点

  1. モジュールの分割粒度

    • 大きすぎず小さすぎない適切な粒度
    • 機能的な凝集度の高さ
    • 明確な責任範囲
  2. テスタビリティ

    • モックとスタブの適切な使用
    • テストカバレッジの維持
    • E2Eテストの重要性
  3. パフォーマンス考慮

    • N+1問題の回避
    • キャッシング戦略
    • 非同期処理の適切な使用

まとめ

これらの設計原則は、以下を実現するための基盤となります:

  • メンテナンス性の高いコードベース
  • スケーラブルなアプリケーション
  • 堅牢なエラー処理
  • 効率的なチーム開発

プロジェクトの要件や規模に応じて、これらの原則を適切に組み合わせることが重要です。

参考文献

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?