飽き性の頭の中

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

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

tawachan
tawachan

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

NestJS でのモジュール分割

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

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

  • UserModule
  • OrderModule
  • ChatModule

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

assets/20200128194110.png

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

相互依存がひどくなった

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

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

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

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

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

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

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

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

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

assets/20200128195621.png

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

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

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

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

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

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

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

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

assets/20200128200438.png

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

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

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

関連記事