Wantedly Engineering Handbook
  • Wantedly Engineering Handbook
  • まえがき
  • 第一部:開発チームへの案内
    • 技術とアーキテクチャ
    • プロダクト概要(未執筆)
    • 開発チームの構造
    • コミュニケーションの全体
    • ドキュメンテーション(未執筆)
    • カレンダー
    • 障害対応の心構え
    • 効率的な社内知識の調べ方
    • 外部発信(執筆中)
  • 第二部:技術領域への案内
    • Apps
      • アプリを提供するプラットフォーム
      • デザインシステム入門
      • Web アプリのアーキテクチャ
      • プロダクトデザイナーと上手に協働するための心得
      • Web アプリのデザインシステムライブラリ
      • Web アプリ共通ライブラリ "React Shared Component" の紹介
      • モバイルアプリのアーキテクチャ
      • モバイルアプリのデザインシステムライブラリ(未執筆)
    • The System
      • protobuf スキーマと gRPC 通信
      • 実践: gRPC in Ruby
      • 実践: gRPC in Go
      • GraphQL Gateway - アプリ向けに API を公開する
      • Wantedly Visit で BFF GraphQL サーバーを辞めた理由
      • 実践: GraphQL スキーマ設計(未執筆)
      • API での認可処理 (Authorization)
      • マイクロサービス共通ライブラリ "servicex" の紹介
      • 非同期メッセージング処理入門(未執筆)
      • バッチ処理入門(未執筆)
    • Infrastructure
      • Infrastructure Squad
      • プロダクト開発のための Kubernetes 入門
      • インフラ構成概要
      • リリース・デプロイ戦略を支える技術
      • SaaS を活用する:New Relic, Honeybadger, Datadog
    • Data
      • データ基盤入門
      • レコメンデーション
      • Looker 入門
      • 推薦システムの開発に使っているツール
    • 開発プロセス
      • Git の慣習
      • ポストモーテムの取り組み
      • 負債返済日の取り組み
      • プロダクトの課題発見及び解決
      • ソフトウェアデザインの基礎
      • コードレビュー
      • コーディング規約
      • リリース・デプロイ戦略
      • 上長承認が必要な作業
      • アーキテクチャディシジョンレコード(ADR)
      • 作業ログを残す意味
      • 多言語化対応(i18n)
      • メール開発
    • 開発ツール
      • 社内利用している開発ツールの最新状況
      • kube
      • Code Coverage
      • Kubefork
  • おわりに
    • ロードマップ(未執筆)
    • Handbook の書き方
    • コントリビューター
  • 付録
    • 社内用語集
    • 主要な GitHub レポジトリのリスト(未執筆)
    • 今後の挑戦・未解決イシュー(未執筆)
    • プロダクト開発組織のバリュー(未執筆)
    • 採用についての考え方(未執筆)
GitBook提供
このページ内
  • GraphQL Gateway とは?
  • なぜ GraphQL なのか?
  • どのような問題を解決できるか
  • どのように問題を解決するか
  • GraphQL Gateway の開発フレームワーク
  • スキーマ設計について

役に立ちましたか?

  1. 第二部:技術領域への案内
  2. The System

GraphQL Gateway - アプリ向けに API を公開する

前へ実践: gRPC in Go次へWantedly Visit で BFF GraphQL サーバーを辞めた理由

最終更新 1 か月前

役に立ちましたか?

Wantedly では、システム内部のマイクロサービス間通信に Protocol Buffers / gRPC を利用しています(『』の章)。

では、他のマイクロサービスではなく、Webアプリやモバイルアプリに向けて API を提供する場合についてはどうすると良いでしょうか?

この章では、アプリから使えるシステムの API(まさに "Application Programming Interface" です)を用意する際に使う、GraphQL Gateway について概説します。

GraphQL Gateway とは?

GraphQL Gateway とは、システムの中に数あるマイクロサービスの一つで、アプリ向けに GraphQL API を提供するものです。

基本的に、アプリからシステムに対しての API 呼び出しは全て GraphQL Gateway が引き受けることを想定しています(まだこのアーキテクチャに移行していない機能は多いので、それらは直接各マイクロサービスの REST API を叩いています)。したがって、システムの中でも前段に立つコンポーネントであると言えるでしょう(「」の章の図も併せてご参照ください)。

理想状態として、この GraphQL Gateway はシステムが持つドメインオブジェクトとそれに対する操作を全て提供します。従って、アプリから見ると、システムのインターフェイスは GraphQL になっており、そこでできることは GraphQL スキーマを見ることによって探索できる状態になります(実際には、GUI である (内部向けリンク)を利用することになるでしょう)。そして、スキーマを見ながら、アプリは自ら欲しいデータをクエリによって選択的に取得します。

動作としては、GraphQL Gateway は受けたリクエスト内容をもとに後段のマイクロサービスに対して gRPC の呼び出しを行います。この結果を集約し、オブジェクトグラフを構築してレスポンスとして返却します。後述するように、この作業はフレームワーク化されているため、全てをマニュアルで記述する必要はありません。

なぜ GraphQL なのか?

マイクロサービス間通信に Protocol Buffers / gRPC を使っていながら、なぜアプリとの通信では GraphQL を利用するのか。この「目的」の部分を理解することが、GraphQL で実際にスキーマ設計を行う際などにも非常に重要となります。

それは一言で言えば、ビジュアルデザインやレイアウトデザインに依存しない、情報設計の抽象度で API を提供することで、全体の ソフトウェアデリバリのパフォーマンスを最大化する ためです。

どのような問題を解決できるか

具体的に見ていきましょう。技術とアーキテクチャの章の図からもわかるように、Wantedly には複数のアプリが存在します。

Visit サービスに関して言うと、ツー・サイド・マーケットプレイスのサービスのため、個人ユーザーと企業ユーザーの2種類が存在します。これらはそれぞれ別の Web アプリを利用します。また、個人ユーザー向けには Web アプリと同じ機能(あるいはそのサブセット)を iOS アプリと Android アプリにも提供しています。つまり、マルチ・プラットフォーム展開をしています。

サービス特性上、これら複数あるアプリに同じコンテンツが表示(あるいは編集)されます。例えば、個人ユーザーが作成した「プロフィール」や、企業ユーザーが作成した「募集」などです。

つまり、情報としては同じ情報がさまざまなアプリに出ることになります。ただ、画面の大きさ(モバイルかどうか)や個人ユーザー向けか企業ユーザー向けか、などによってレイアウトや情報量は変わります。

こういった、いろいろあるアプリごとに API を作っていては、その度にバックエンドとフロントエンドでのコミュニケーションが発生してしまい、ソフトウェアデリバリのパフォーマンスが下がってしまいます。

新機能を追加する例

一つの理想として言うならば、ある機能を新たに追加する際、次の図のようにシステムの実装は最初の一度きりであるべきでしょう。例えば Visit に対して「副業意欲・転職意欲」を表示・入力できるようにするとします。これの開発のタイムラインを単純化して図示すると、次のようになります:

ここで見落としがちなのは、アプリのエンジニアだけで実装できることで、「実装期間の短縮」「コミュニケーションコスト」だけではなく、バックエンドも含めた「リソースの調整」が不要となる点です。こういったリソース・アロケーションの疎結合化が、組織全体のソフトウェアデリバリのパフォーマンスを押し上げます。

これを実現するためにシステムの設計・実装は、“いま目の前にあるアプリ”のことだけでなく、実装しようとしているオブジェクトが“将来的に別のアプリや別の画面で使われるときのこと”も想定する必要があるので留意しましょう。

グロースを行う例

別の例として、こういった機能のゼロ→イチのフェーズだけでなく、グロースのフェーズでも GraphQL によって情報設計のレベルで分離することはソフトウェアデリバリの速度を向上させます。

例えば、コロナ禍にあってカジュアル面談のオンライン化のニーズが増えた結果、募集の一覧画面に「オンライン面談できるよ!」というタグを出す A/B テストをするとしましょう。この場合、「募集」という GraphQL オブジェクトのフィールドとして「オンライン面談できるかどうか」があるでしょうから、Web アプリの GraphQL クエリを少し編集するだけで実装ができるため、実装からリリースまで1日以内に行うことが可能になるでしょう。

以上は、アプリのソフトウェアデリバリに着目した例ですが、技術領域を跨いだコミュニケーション回数の削減は当然バックエンドの負担も減らすため、バックエンドのシステム改善を加速させることが可能となります。

どのように問題を解決するか

技術的になぜ GraphQL が前述のような問題を解決できるかというと、「スキーマ」や「クエリ言語」といった仕組みを仕様に取り込むことで可能になっています。

これは責務の観点から説明すると、「どの範囲のデータをクライアントが必要とするか」の詳細をサーバーサイドではなく、クライアントサイドがより決められるようになっている、ということでもあります。

付随的に、「スキーマ」「クエリ言語」の仕組みがあることで、サーバーサイド・クライアントサイドにおける型定義の生成などの開発者体験の向上が可能になっています(これ自体は、Protocol Buffers からも open API 経由で似たようなことはできますが)。

GraphQL Gateway の開発フレームワーク

上記のようなことを実現するための GraphQL Gateway のフレームワークについて概説します。

基本的に、GraphQL Gateway は GraphQL Helix という GraphQL サーバー上で Nexus というスキーマ構築ライブラリを利用して記述します。ただし、背後のマイクロサービスとの繋ぎ込みを簡易にするため、Protocol Buffers のスキーマを活用しているという特徴があります。それをここで説明します。

概念的には、GraphQL スキーマはオブジェクトグラフとそれに対するエントリポイントに分けて考えられます。さらに、オブジェクトグラフの方は、ノードに相当する「オブジェクト」とエッジに相当する「リレーション」があります。そして、エントリポイントは、クエリとミューテーションの二種類が存在します。したがってスキーマとして定義すべき要素は次のようになります。

  • グラフ自体…オブジェクト、リレーション

  • グラフに対するエントリポイント…クエリ、ミューテーション

  1. オブジェクトの定義を Protocol Buffers 上できちんと固めて apis にマージする

  2. それを参照しつつリレーション、クエリ、ミューテーションがあれば GraphQL Gateway 上で定義する(実装もここで行う)

の2ステップとなります。なお、「あれば」と書いたのは、例えば既存のオブジェクトに新しくフィールドを追加するだけのことも多いためです。この場合は、Protocol Buffers からのオブジェクト生成を再実行したコードをマージするだけになります。

定義の流れとしては以上のようになります。実装としては、リゾルバ(GraphQLオブジェクトをどのように取得するかのロジック)を書くことになり、そこに gRPC の呼び出しコードを書く(gRPC の RPC はステップ1で一緒に定義してしまうことが多い)という形になります。次のコードは、projects というクエリの定義と、対応するリゾルバの実装になります。

// src/schema/projects/projects.ts

export const projects = queryField("projects", {
  type: nonNull(list(nonNull("Project"))),
  description: "募集一覧に表示する募集の一覧",
  async resolve(_root, _args, ctx, _info) {
    const req = new ListProjectsRequest();
    const resp = await ctx.dataSources.projects.project.promises.listProjects(req, ctx.getGrpcMetadata(info)); // 新しい gRPC サービスが増える場合は、別途 dataSources のマッピングを更新
    return resp.getProjectsList();
  },
});

このように GraphQL Gateway はデータの集約(アグリゲーション)を行いますが、ドメイン知識 (ドメインロジック, ビジネスロジック) は一切持たないので注意しましょう。

スキーマ設計について

最後に、GraphQL の 最も重要な部分はスキーマ設計 になります。

冒頭、GraphQLを入れる理由としてソフトウェアデリバリの全体的な向上を挙げましたが、それを達成できるかどうかは良いスキーマを設計できるかどうかに掛かっていると言えます。

そして、いろいろなアプリで使われる API ということは、一度出してしまったスキーマはなかなか変更できない、ということも同時に意味します(全く変更できない訳ではありませんが、コストが高いという意味です)。

話を聞きに行きたい

もっと知りたい

このうち、一番基礎となるのがオブジェクトですが、これは で定義した Protocol Buffers のメッセージ定義から(コメントなどのメタデータも含めて)自動的に生成されます。なので、GraphQL スキーマを定義する順序としては、

以上は概説になります。より実践的なハンズオンはにあるので、コードを書く際はそちらを参照してください。

スキーマ設計は非常に重要なため、別途の章を設ける予定です。ただ、GraphQL スキーマについて一般的なことは、『』という本の前半部分にまとまっているので、スキーマの設計を多くやる場合は、目を通しておくことをお勧めします。

Slack:

(internal)

protobufスキーマとgRPC通信
技術とアーキテクチャ
GraphQL Voyager
wantedly/apis (internal)
リポジトリ配下のドキュメント
Production Ready GraphQL
#backend_chapter
GraphQL Gateway docs
みんなで育てる GraphQL スキーマ, それを支える Protobuf - Speaker Deck
技術を的に当てる技術について - GraphQL を入れ直した話 - Speaker Deck