1
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

はじめに

「最初はControllerとService1個ずつでいいと思ってた」

NestJSで個人開発してたとき、最初は小さな機能だったから特に設計も気にせず進めてた。
だけど、ユースケースが増えるたびにモジュールが肥大化していき、
気づけば「このモジュール、誰も触りたくないやつ」になってた。

この記事では、そんな モジュール肥大化で詰んだ実体験と、
それを どうリファクタリングして乗り越えたかをまとめておきます。


🧩 最初の構成(小さく始めたつもりだった)

src/
└── user/
    ├── user.module.ts
    ├── user.controller.ts
    ├── user.service.ts
    └── user.entity.ts

💥 詰んだ構成

最初はこんな感じだった。

  • Controller にAPI10個以上
  • Service にロジックどんどん生やす
  • Entity をそのままレスポンスに使ってた

正直これで快適だった。が……。


💣 詰んだ瞬間

  • user.service.ts500行超え
  • createUser()分岐だらけ(招待コードあり/なし、キャンペーン経由など)
  • Entityをそのまま返してたせいで、Prismaの構造が外にモロバレ
  • テストしようとしたら、DB依存が強すぎて単体化できない
  • フロントの仕様変更がくるたびに、コードの影響範囲が爆発

🛠️ モジュール分割でやったこと

✅ ユースケースごとに Service を分けた

src/
└── user/
    ├── use-case/
    │   ├── create-user-with-invite.service.ts
    │   ├── sync-user-profile.service.ts
    │   └── ...

→ 「1ファイル=1目的」にしたら、思考も整理されてテストもしやすくなった。


✅ DTO と Entity を完全に分離

  • user.entity.tsPrismaモデルに専念
  • dto/ フォルダを作って、CreateUserDto, UserResponseDto を分離
  • クライアントに返すレスポンスも 明示的に型定義できるようになった

✅ Mapper を導入して変換処理を明確化

// user.mapper.ts
export class UserMapper {
  static toDto(entity: UserEntity): UserResponseDto {
    return {
      id: entity.id,
      name: entity.name,
      email: entity.email,
      // ...
    };
  }
}

→ これで「どこで Entity → DTO になるか」が一目で分かるように。


📐 リファクタ後の構成(抜粋)

src/
└── modules/
    └── user/
        ├── user.module.ts
        ├── api/
        │   ├── user.controller.ts
        │   └── dtos/
        ├── use-case/
        │   ├── create-user-with-invite.service.ts
        │   └── sync-profile.service.ts
        ├── core/
        │   ├── entities/
        │   ├── repositories/
        │   └── mappers/

  • ControllerAPI専用
  • UseCase層 に処理を集約
  • Domainっぽい形 で整理が効くように

🔁 やって良かったこと/やりすぎたこと

✅ やって良かった

取り組み 理由
ユースケースごとにService分割 テスト・リーダブル・拡張性全部向上
DTOとEntityの完全分離 Prismaに引きずられないAPI設計ができた
Mapper導入 データ変換の責任範囲が明確になった

⚠ やりすぎたかも

取り組み 理由
Serviceが細かくなりすぎた ファイル数が爆増。探すのが手間(命名でカバーした)
Mapperに処理詰めすぎた 共通化しすぎて逆に再利用性が落ちた場面もあった

✅ まとめ

  • NestJSは構造化しやすい反面、モジュールを育てすぎると破綻する
  • 特に個人開発だと、最初は雑に始めがち
  • でも、後から整理しても全然間に合う
  • モジュールが大きくなったら、**「ユースケース単位でServiceを切る」**のが第一歩

🙌 最後に

設計って「最初から完璧」にできることはほぼない。
でも、詰んだ時に構成を見直せるかどうかで、その後の開発体験が全然変わる。

自分みたいに「やばい、Service肥大しすぎた…」ってなった人の参考になればうれしいです。

1
0
1

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
1
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?