飽き性の頭の中

福岡から帰ってきて東京で暮らす26歳。単なる文字の記録。

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

f:id:tawachan39:20200128202017p:plain

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

NestJSでのモジュール分割

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

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

  • UserModule
  • OrderModule
  • ChatModule

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

docs.nestjs.com

f:id:tawachan39:20200128194110p:plain
イメージ図

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

相互依存がひどくなった

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

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

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

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

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

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

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

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

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

f:id:tawachan39:20200128195621p:plain
相互依存・循環参照してしまう

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

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

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

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にまとめてしまうのも手かもしれませんが、モジュールごとに分けておくとテストもしやすいと思うので分ける方向でやりました。

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

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

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

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

f:id:tawachan39:20200128200438p:plain
コントローラーとサービスを別のモジュールに

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

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

./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はまだ新しく知見があまりないので、いろんな情報が増えてくるといいなと思っています。何かあれば逆に指摘等もらえたら嬉しいです。