Wantedly Visit で BFF GraphQL サーバーを辞めた理由

概要

ウォンテッドリーでは2019年頃から(Web・モバイル)アプリとシステムの通信に GraphQL を使用しています。この章では GraphQL 導入に至った背景とこれまでの歴史について紹介します。
紹介する内容
  • Backend For Frontend (BFF) として導入された GraphQL サーバー
  • 我々の GraphQL サーバーは BFF ではなく API Gateway であるという解釈変更
  • 解釈変更に伴って生まれた新しい GraphQL サーバー

Backend For Frontend (BFF) として導入された GraphQL サーバー

最初の GraphQL サーバーがウォンテッドリーに登場したのは 2019 年初頭のことです。募集作成画面という機能をリアーキテクチャ含めたリニューアルをするタイミングで導入されました。このリアーキテクチャでは主に以下のことを実施しました。
  • フロントエンドの刷新。Ruby on Rails で書かれていた UI を、独立した React 製のアプリケーションへと移行する。
  • バックエンドの刷新。募集作成に関する機能を Wantedly のマザー Rails から切り離し、マイクロサービスへと移行する。
  • フロントエンドとバックエンドとの通信のために、GraphQL サーバーを導入する。
老朽化しメンテナンス性の低くなっていたフロントエンドのコードベースを、独立したリポジトリに移行することで改善サイクルを回しやすくするという目的がありました。またバックエンドに関しては People の方で成功していたマイクロサービス化を Visit の方にも取り入れメンテナンス性の向上などを図ろうと考えていました。 システムがマイクロサービス化していくとフロントエンド側から見ると、取得したいリソースごとにリクエストするサービスを変えなくてはいけないというデメリットが生じます。あるリソースは JSON/HTTP を使ってサービスAにリクエストするが、別のリソースは ProtoBuf/HTTP を使ってサービスBにリクエストしないといけない、といった具合です。 こういったデメリットを解消するためアプリとシステムの間に GraphQL サーバーを置くという判断になりました。GraphQL がシステムの入り口にいると、GraphQL のクエリをインターフェースとして後ろにいるシステムが抽象化されます。
募集作成のリニューアルのあとも、別の機能のリニューアル・マイクロサービス化を含むリアーキテクチャが行われる予定がありました。これから増えていくマイクロサービスのリクエストの口として効果を期待されて導入されました。
導入初期はあくまでも募集作成画面を含む採用管理画面のための GraphQL サーバーという立ち位置であり、フロントエンドのためにシステムとの通信を肩代わりする役割として存在していたため Backend For Frontend (BFF) として認識されていました。

BFF ではなく API Gateway であるという解釈変更

最初の GraphQL サーバーを数年運用しているといくつか問題が見えてきました。
  • システムの Protobuf 定義と GraphQL スキーマ管理による二重のスキーマ定義
  • GraphQL と Protobuf の値の不毛な変換処理

システムの Protobuf 定義と GraphQL スキーマ管理による二重のスキーマ定義

ウォンテッドリーでは各マイクロサービスは基本的に gRPC を採用しており、Protobuf を使って通信を行っています。.proto ファイルで rpc 定義が書かれており、これは中央集権的に管理されています。各機能の開発するときにはまず .proto ファイルに rpc や message 定義を記述します。この .proto ファイルから protoc を使って生成したコードを使ってクライアントを開発します。 GraphQL サーバーを開発するときもまずはスキーマを定義します。ウォンテッドリーでは nexus を使ってコードファーストにスキーマを定義しています。お気づきのように、ここでスキーマの二重定義が発生してしまっています。.proto ファイルでシステムの rpc を定義し、それを GraphQL サーバーから呼び出せるように GraphQL スキーマに翻訳する必要があるのです。 システム側での定義が完了したと思ったら、アプリから呼び出すためには GraphQL の方でも同じような作業をしないといけない状況で、不要な開発コストが発生してしまっていました。

GraphQL と Protobuf の値の不毛な変換処理

さらに開発上で煩雑だったポイントとして、GraphQL の世界と Protobuf の世界の間での不毛な値の変換が発生してしまっていたことがあります。特に Enum 周りの変換が不毛で gqlToProtoFooprotoToGqlFoo といった関数が大量に発生してしまいました。

解釈変更

もともと Backend For Frontend として生まれた GraphQL サーバーですが、実際のところ、このレイヤーでフロントエンドアプリケーションのための処理というのは行われていませんでした。なるべくドメインロジックを載せないようにという方針も相まって、最初に生まれた GraphQL サーバーは実態としては API Gateway でした。 また、もともとウォンテッドリーでは Web で成功した機能をモバイルアプリに展開するというのがよくある流れでした。このときに、Web で使っていた WebAPI をそのまま流用するのではなく、モバイルアプリ用に追加実装するということが起きていました。しかし理想的には一つの機能については一つの WebAPI のみが提供されているべきです。 cf. https://docs.wantedly.dev/fields/the-system/graphql-gateway#wosuru 上記背景から我々の GraphQL サーバーは BFF として存在するのではなく、API Gateway として存在するべきだという解釈に変更になりました。

解釈変更に伴って生まれた新しい GraphQL サーバー

解釈変更に伴い、新しい GraphQL サーバーが立ち上がりました。これは前の GraphQL サーバーが抱えていた課題を解決するために生まれたものです。
  • .proto ファイルから nexus の DSL を生成し、スキーマの二重定義と不毛な値の変換処理記述を解消
新しい GraphQL サーバーでは proto-graphql-js というツールを使って、.proto ファイルから nexus の DSL を生成しています。ここで生成された DSL をベースに、さらにスキーマ定義を拡張したり、DataLoader を実装することで、一つの GraphQL スキーマとして動作するようになっています。 ここに関しての説明はGraphQL Gateway の開発フレームワークに詳しく書いてあります。