🐱

NestJSでのモジュール分割。相互依存がつらいので細かく分けた話

2020/09/22に公開

こんにちは、たわです。今回はNestJSでの開発での話です。

NestJSでのモジュール分割

NestJSではモジュール分割をすることができます。Angularにちなんでとのことです。

公式ドキュメントをみると、

  • UserModule
  • OrderModule
  • ChatModule

というようにオブジェクトの種類ごとにモジュールを分割する感じで書いてあります。

ですが、そのイメージで開発を進めていくとつらいかもしれません。個人的に直面した状態を説明します。

相互依存がひどくなった

僕の場合は、モジュール同士の相互依存が多くなってきました。循環参照は技術的には回避する術はありますが、複雑になると全体像が見通せなくなるので、改修しづらくなると思いました。

サービス同士の依存はない

サービスは担当しているオブジェクトを扱うだけなので、基本的に他のサービスと依存することはないと思います。

なので、順当に開発していればそこはそんなに問題ではないでしょう。

コントローラーの依存が多い

ですが、コントローラーは、いろんなサービスを使います。

例えば、UsersControllerであれば、UsersServiceのみならず、OrdersServiceも使うことになるでしょう。

また、OrdersControllerも関連するユーザー情報を扱うためにUsersServiceを使うはずです。

しかし、コントローラーもサービスも同じモジュール内に入っているので、相互依存することになってしまうのです...。

新しいモジュールを追加するのがつらい

そうすると実際何が辛かったか。一番大きかったのはモジュール追加ができないことです。

すぐに以下のようなエラーが出て依存関係が解決がとても難しくなりました。

Error: Nest can't resolve dependencies of the ApiUsersController (?). Please make sure that the argument UsersService at index [0] is available in the ApiUsersControllerModule context.

Potential solutions:
- If UsersService is a provider, is it part of the current ApiUsersControllerModule?
- If UsersService is exported from a separate @Module, is that module imported within ApiUsersControllerModule?
  @Module({
    imports: [ /* the Module containing UsersService */ ]
  })

慣れればできるかもしれませんが、新しく来た開発者の参入障壁が無駄に上がってしまうので、なんとか避ける必要がありました。

コントローラーとサービスを別のモジュールにする

解決策としては、コントローラーとサービスを別のモジュールにすることにしました。

すべてをルートのAppModule.tsにまとめてしまうのも手かもしれませんが、モジュールごとに分けておくとテストもしやすいと思うので分ける方向でやりました。

かなり見通しが良くなった

こうすることでかなり依存関係の見通しがよくなりました。

図の通り、必ず矢印が下から上になるようになりました。コントローラーは実際に使用しているサービスのモジュールをインポートするというシンプルな仕様になりました。

これであれば初めて当コードを触る人でも対処できるのではと思います。

ディレクトリ構成は機能別

ディレクトリ構成としては以下のような感じになります。コントローラーやサービスごとにフォルダを作る感じにしています。

./src
├── main.ts
├── app.interface.ts
├── app.module.ts
├── controllers
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── chats
│   │   ├── chats.api.controller.module.ts
│   │   └── chats.api.controller.ts
│   ├── orders
│   │   ├── orders.api.controller.module.ts
│   │   └── orders.api.controller.ts
│   └── users
│       ├── users.api.controller.module.ts
│       └── users.api.controller.ts
└── services
    ├── bots
    │   ├── bots.service.module.ts
    │   └── bots.service.ts
    ├── chats
    │   ├── chats.service.module.ts
    │   └── chats.service.ts
    ├── orders
    │   ├── orders.service.module.ts
    │   └── orders.service.ts
    └── users
        ├── users.service.module.ts
        └── users.service.ts

moduleの数が増えて細かいファイルをたくさん作るのが面倒な感もありますが、シンプルになるのでとりあえずいいかなと思っています。

まとめ

実際の新規開発でNestJSを使ってみて困ったこととそれへの今の所の解決策をまとめてみました。NestJSはまだ新しく知見があまりないので、いろんな情報が増えてくるといいなと思っています。何かあれば逆に指摘等もらえたら嬉しいです。

主な記録はこちらに投稿しています。

https://blog.tawa.me/entry/nestjs-module-splitting

Discussion