はじめに
株式会社Schooでエンジニアをしている、福嶋淳一(@junichi_fukushima_schoo )と申します。プロジェクトリーダーとしてチームの開発を推進する役割を担いつつ、自身でも手を動かして開発を行っています。
何を書いたか
弊社では、今年の1月からシステムリプレイスのプロジェクトが走っています。そのプロジェクトではBFFパターンを採用し、「Hono×GraphQLサーバー」という技術をチョイスしました。導入実績のない技術に挑戦し、導入後の気づき・学びについて整理しました。
※弊社では、システムリプレイスの進め方として段階移行方式を採用しており、今月9月に第1回目のリリースを実施することができました🎉🎉🎉🎉
なぜ記事を書こうと思ったのか
-
理由①: BFFパターン・Hono×GraphQLサーバーを採用・運用してのメリット・デメリットを共有したい
-
理由②: 技術選定のプロセスについて、良かった点・課題を共有したい
記事の流れ
以下の流れで話を展開していきます。
- 技術選定の背景
- 弊社におけるBFFのユースケース
- 良かったこと
- 技術的側面
- プロセス面
- 課題
- 技術的側面
- プロセス面
- まとめ
技術選定の背景
※詳しい技術選定の背景や、どのように技術選定を進め開発を進めていったのかについては、以下の資料に詳しく記載しています。詳細が気になった方はご覧いただけると幸いです🙏
そもそもBFFとは何か
BFFは、フロントエンド向けに最適化された専用サーバーを置くアーキテクチャです。バックエンドの複数のAPIをまとめて呼び出したり、データを加工したりすることで、フロントエンドの要求に効率的に応えます。
システム全体の技術スタック
本プロジェクトでは、フロントエンド・BFF・バックエンドの3層構造になっており、以下のような技術スタックを採用しています。
なぜBFFを採用したのか
- バックエンドAPIは各ドメインに分かれているため、複数のAPIを組み合わせるケースが発生します。BFFはこれらを統合し、フロントエンドに最適な形でデータを提供します
- 過去にフロントエンドの処理負荷が課題となった経験から、以下の処理をBFF側に寄せたいと考えました
- gRPC通信などの複雑なプロトコル処理
- DTOの変換・整形処理
- 認証・認可ロジック
なぜHono×GraphQLなのか
-
Hono
- 軽量・シンプルな設計により、将来的な技術スタックの変更にも柔軟に対応可能
- 分かりやすいAPI設計で学習曲線が緩やか
- 優れたパフォーマンスで高速なレスポンスを実現
-
GraphQL
- 本プロジェクトではBFFサーバー側でGraphQLサーバーを実装し、フロントエンドからのクエリに応答する構成を採用しています
- クライアント側で必要なデータの構造を指定でき、オーバーフェッチやアンダーフェッチを防げる
- 複数のエンドポイントへのリクエストを1つのクエリに集約可能
弊社におけるBFFのユースケース
① DTO層としての役割
バックエンドAPIは各ドメインに分かれているため、それらの情報を集約し、フロントエンド側の柔軟な要求に対応できるDTO層としての役割を担っています。
② 認可としての役割
BFFではフロントエンドから受け取った認証情報を検証し、認証サービスに対して認証情報の検証を行った後、認可の実施を行います。本プロジェクトにおける認可の処理ロジックはBFFに集約されています。
このような形をとることで、以下のようなメリットがあります。
-
バックエンド観点
- 認可エラーの場合、早期リジェクトによりバックエンドまでリクエストを送らずに済むため、パフォーマンス向上が見込まれる
-
フロントエンド観点
- 認可ロジックをフロントエンドから排除することにより、シンプルにAPIを呼ぶだけになりUI実装に専念できる
- 認可ロジックがブラウザに露出しないため、セキュリティの向上が見込まれる
良かったこと
技術的側面
BFFパターンを採用したことによるメリット
- フロントエンド/バックエンドをシンプルにすることができた
- DTO層/認可を行う層としてのBFFを挟むことで、
- フロントエンド → UI/表示ロジックに専念できた
- バックエンド → ドメインロジック、データ永続化に専念できた
- DTO層/認可を行う層としてのBFFを挟むことで、
Honoを採用したことによるメリット
- 軽量・シンプルな設計
- 学習コストが低い
- 必要最小限かつ特殊な機能がないので、スムーズに開発を進めることができた
GraphQLを採用したことによるメリット
- データの構造を柔軟に指定し、フロントエンドの要求に応えることができた
- 接続先が1個なので、「このデータを取るにはどのエンドポイントなのか?」という開発時の迷いが減り楽になった
プロセス面
- 技術の比較 → PoCの実施 → 社内勉強会の実施 → 1個目の機能作成時はモブワークを実施するという流れにより、開発メンバー全員が技術に対しての理解を深めることができた
- 技術の比較 → PoCの実施をすることで、全員が必要性を実感・納得した上で技術選定を行うことができた
- モブワークを実施することで全員の知識レベルの底上げを行うことができ、フロントエンドの開発が佳境なときに、バックエンドのメンバーがサポートに入ることが可能になった
課題
技術的側面
パフォーマンス課題:Node.jsによるGraphQLサーバーを採用したことによるデメリット
今回のプロジェクトでは、10年先でもシステムが維持できるような次世代プラットフォームの構築を目指しています。そのため、高いパフォーマンス目標を定め、その目標達成のために負荷試験を継続的に実施していました。その負荷試験を実施する中で、BFFのCPU負荷が高まり、レイテンシーが悪化していることが発覚しました。
原因とその対応策について説明していきます。
-
GraphQLの内部処理が重い
- プロファイラを見ると、
node_modules/graphql/execution/execute.jsというファイルがかなりCPU負荷をかけている - この
execute.jsは、クライアントからクエリを受け取り、resolverを呼び出す前に行う以下のような一連の処理です
- プロファイラを見ると、
→ Parser/Validation/execute結果をコンパイルしキャッシュすることで、この問題を回避しました。
-
Node.js側のイベントループの処理がキューに溜まり続け、CPU負荷が上昇しレイテンシーの悪化を招いた
- Node.jsはシングルスレッドだが、処理をブロッキングせずに他の処理も並行して行うイベントループの仕組みがある
- 後回しにされた処理はキューイングされる
- 実行すべき処理が溜まり続けるにも関わらず、後続のリクエストが来続ける
- Node.jsはシングルスレッドだが、処理をブロッキングせずに他の処理も並行して行うイベントループの仕組みがある
→ 一定の負荷が来たときに、スケールアウトする形で対応しています。
→ 今後は状況を見て、clusterで複数ワーカーを立ち上げ、複数のプロセスを生成して負荷分散を実現することなどを検討していく予定です。
プロセス面
- プロトタイプの作り込みの甘さ
→当然ではあるものの、新しい技術を採用するときはプロトタイプを作り込む必要がある
→実際の機能を1個作り、負荷試験まで実施すると良いのではという意見が上がりました
まとめ
成果
技術面での成果
- フロントエンド・バックエンドそれぞれが本来の責務に集中できる構成を実現
- Honoの学習コストの低さにより、チーム全体がスムーズに開発に参加
組織・プロセス面での成果
- PoC → 勉強会 → モブワークという段階的な導入により、チーム全体のスキルアップを実現
- フロントエンド・バックエンドエンジニアの垣根を越えた協力体制の構築
最後に
新技術の導入は常にリスクを伴いますが、以下のアプローチが有効でした。
- 小さく始めて大きく育てる:PoCから始めて段階的に拡大
- チーム全体を巻き込む:技術選定の段階から全員参加で進める
- パフォーマンスは早期に検証:本番相当の負荷試験を早い段階で実施
BFFパターンは、フロントエンドの複雑性を解消する有効な手段です。また、Hono×GraphQLの組み合わせは、その実装において軽量かつ柔軟な選択肢として推奨できますが、Node.jsによるGraphQLサーバーのパフォーマンス特性には注意が必要です。
株式会社Schooでは一緒に働く仲間を募集しています!



