NestJS と gqlgen ~DIの観点から~

NestJS と gqlgen ~DIの観点から~

目的

  • 次のプロジェクトで graphql を採用することが決まった。そこで TypeScript か Golang (自分が書きやすい言語という意味で) のどちらにしようか調べてた
  • つまり gqlgen と NestJS のどっちのほうが開発体験がよさげなのかをまとめておくことにした

もしプロジェクト開始の段階で悩んでいらっしゃる方がいたら参考になればコレ幸い!!

実際に Tutorial やるまえの先入観 ( 公式 docs は読まずにネットで調べただけの知識 )

NestJS

  • デコレータ書きまくるのがちょっとなぁ...
    • デコレータ恐怖症ってのとデコレータって何やってるか不明瞭になること多くない??という疑念
  • inspired by Angular... Angular ねぇ 🤔
  • DI したいだけやのにいろいろと大げさに見える
  • TypeORM 押出杉では??
    • 若干の rails for TS 感を感じる
    • 逆に TypeORM してない firestore とか spanner を repository pattern 的に使いたいときはどうすれば??
  • NestJS + NextJS (んまぁこれは NextJS に限らずやけど) だと TS only でいけるのでサイコー
    • コレほんまかいな??
  • メルカリが採用してる からよさそう (情弱感)

gqlgen

とりあえずコード

https://github.com/jupemara/graphql-languages

に試したやつは置いときました。んでは早速両方よかったところ、悪かったところを見ていきましょう

実際にチュートリアル+DI をやってみた

NestJS

行った手順

  1. 公式のチュートリアル からサンプル作って、 graphql の resolver も作ってみてそこに interface で DI してみた

https://github.com/jupemara/graphql-languages/tree/main/nestjs においてあります

メリット

  • デフォで TS なのはとてもいい!!
  • デフォでいろんなものがシングルトンになるってのはいいですね!! DI コンテナと相性がいい
  • providers を使った DI コンテナ (といって良いんかな) が自分が思ってた以上にさらっと使えてよかった!
  • コードファーストを採用すればマイクロサービス開発の速度向上、各チームのコリオグラフィーをバク上げできそうな気がした
  • フレームワークに乗っかれば、DI も自分で書かなくてええんで、constructor 呼び出すときに new XXX() のネストが続くこともなくなるね
    • DI のボイラープレートっておんなじこと毎回書いてる感が否めないのでここはスッキリして良いかも
  • サービスのバウンダリーごとにディレクトリを切っていくイメージになるので、 src/author , src/todo みたいな感じで サービスバウンダリー = マイクロサービスとなって、コードファーストと相性がよさそう
    • 逆にドメインがちっさめだと分割する意味があんまりないので、ここは使い所要注意というかディレクトリ切りすぎ要注意かもですね
  • TypeORM 押出杉ではあったが、自前の repository レイヤーも imports に入れてあげればいいだけなので、んまぁ別にデメリットではない

デメリット

  • nest g 系のテンプレを生成するスクリプト、薄めにできあがるのでそんなに問題にはならんような気はしているが、 breaking change するにはどうするんや感
    • なんとなく yeomen を思い出した
  • デコレータ、なぁ!!!
    • number を Int と Float に書き換える必要があるのでんまぁわかるんやけど function(@Args('id', { type: () => Int})) {} は流石にちょっとキモい感
  • "service" って名前はどうかね!もうちょっと明示的な名前にならんかね!! DDD 的にもドメインサービスって各種ドメインモデルで解決できないときの最終手段として使うとありますしね
    • inspired by Angular は伊達じゃないな
      • とはいえコレも慣れっちゃ慣れ
  • TS の仕様により、 interface から reflection できないってことだそうで、interface に依存する service を作るときは token を文字列で指定するっていうちょっと一手間が必要
    • NestJS 使わずに直接 DI するときは index.ts とかのルートモジュールで DI していくと思うので、ここは案外落とし穴になるかもね
  • DI できる箇所が各種 module 内と、 app.module.ts にあるので、コード量が増えてくると依存関係が不明瞭になりそう
  • デメリットというほどではないけど NextJS と NestJS で TS にまとまったけど、デコレータ書いたりするので、結構別物に感じました

gqlgen

行った手順

  1. github の readme からサンプル作って、 graph/resolver.go 内で DI っぽく書いてみました

https://github.com/jupemara/graphql-languages/tree/main/gqlgen においてあります

メリット

  • 既存のロジックやディレクトリ構造に関係なく純粋な Presentation レイヤーとして graphql を使うことができそう
    • つまり infra/controller/http みたいなディレクトリに近いノリで graph/ を使えそう
  • スキーマファーストと golang の型システムとの相性がよさそう
    • とはいえこのメリットは gqlgen というよりかは、 スキーマファーストと静的型付け言語でアレばなんでも一緒かもですね
  • フレームワークとして薄いので、DI の見通しがいい!
    • schema ファイル内で input を定義するとここの関数の引数として入ってくるので、それをコマンドオブジェクトとして wrap するなり直接渡すなりすれば infra => usecase => domain model みたいな流れがうまく組めそうです

デメリット

  • コレは NestJS 触った後に gqlgen を触った感想だからかもですけど、 DI のボイラープレートに近いコードがめんどくせぇ!
    • 真面目にやると NewXXXService NewXXXController NewXXXRepository と三種を書くことになると思うんですが、それがちょっと('A`)マンドクセ
    • ただコレは逆にいうと、既存の資産をそのままつかえるという話なので、状況によって賛否両論あるなと感じました
      • TS で同じことしたいなら graphql-yoga を使うという選択肢もあるわけですし
  • graph/schema.resolvers.go というファイルができてそこに controller 相当のコードを書いていくんですが、できればここはフレームワーク側でファイル分けてほしかったですね

まとめ

golang で DI するときの NewXXX を書くのに結構疲れてきたので、 NestJS で逝きますね(ニッコリ)。

参考リンク(順不同)

octobot: タコ・ソ・ノモノ