日本語

スキーマスティッチングでGraphQLフェデレーションの力を解き放ちましょう。複数のサービスから統一されたGraphQL APIを構築し、スケーラビリティと保守性を向上させる方法を学びます。

GraphQLフェデレーション:スキーマスティッチング - 包括的ガイド

進化し続ける現代のアプリケーション開発の世界では、スケーラブルで保守性の高いアーキテクチャの必要性が最も重要になっています。マイクロサービスは、その固有のモジュール性と独立したデプロイ可能性により、一般的な解決策として浮上しています。しかし、多数のマイクロサービスを管理することは、特にクライアントアプリケーションに統一されたAPIを公開する際に、複雑さを生む可能性があります。ここで、GraphQLフェデレーション、特にスキーマスティッチングが活躍するのです。

GraphQLフェデレーションとは?

GraphQLフェデレーションは、複数の基盤となるGraphQLサービス(多くはマイクロサービスを代表する)から単一の統一されたGraphQL APIを構築できる強力なアーキテクチャです。これにより、開発者はあたかも単一のグラフであるかのように異なるサービス間のデータをクエリでき、クライアント体験を簡素化し、クライアント側での複雑なオーケストレーションロジックの必要性を減らします。

GraphQLフェデレーションには、主に2つのアプローチがあります:

この記事では、スキーマスティッチングに焦点を当て、その概念、利点、制限、そして実践的な実装について探ります。

スキーマスティッチングを理解する

スキーマスティッチングは、複数のGraphQLスキーマを単一のまとまりのあるスキーマにマージするプロセスです。この統一されたスキーマはファサードとして機能し、基盤となるサービスの複雑さをクライアントから隠します。クライアントがスティッチされたスキーマにリクエストを送信すると、ゲートウェイはインテリジェントにリクエストを適切な基盤サービスにルーティングし、データを取得し、結果を結合してクライアントに返します。

これを次のように考えてみてください:それぞれ異なる料理を専門とする複数のレストラン(サービス)があるとします。スキーマスティッチングは、各レストランのすべての料理を組み合わせたユニバーサルメニューのようなものです。顧客(クライアント)がユニバーサルメニューから注文すると、注文は適切なレストランのキッチンにインテリジェントにルーティングされ、食事が準備され、そして顧客への単一の配達にまとめられます。

スキーマスティッチングの主要概念

スキーマスティッチングの利点

スキーマスティッチングは、マイクロサービスアーキテクチャを採用する組織にいくつかの魅力的な利点を提供します:

スキーマスティッチングの制限

スキーマスティッチングは多くの利点を提供しますが、その制限についても認識しておくことが重要です:

スキーマスティッチングの実践的な実装

Node.jsとgraphql-toolsライブラリ(スキーマスティッチングで人気の選択肢)を使用して、スキーマスティッチングを実装する簡単な例を見ていきましょう。この例では、ユーザーサービス製品サービスの2つのマイクロサービスを使用します。

1. リモートスキーマの定義

まず、各リモートサービスのGraphQLスキーマを定義します。

ユーザーサービス (user-service.js):


const { buildSchema } = require('graphql');

const userSchema = buildSchema(`
  type User {
    id: ID!
    name: String
    email: String
  }

  type Query {
    user(id: ID!): User
  }
`);

const users = [
  { id: '1', name: 'Alice Smith', email: 'alice@example.com' },
  { id: '2', name: 'Bob Johnson', email: 'bob@example.com' },
];

const userRoot = {
  user: (args) => users.find(user => user.id === args.id),
};

module.exports = {
  schema: userSchema,
  rootValue: userRoot,
};

製品サービス (product-service.js):


const { buildSchema } = require('graphql');

const productSchema = buildSchema(`
  type Product {
    id: ID!
    name: String
    price: Float
    userId: ID!  # ユーザーサービスへの外部キー
  }

  type Query {
    product(id: ID!): Product
  }
`);

const products = [
  { id: '101', name: 'Laptop', price: 1200, userId: '1' },
  { id: '102', name: 'Smartphone', price: 800, userId: '2' },
];

const productRoot = {
  product: (args) => products.find(product => product.id === args.id),
};

module.exports = {
  schema: productSchema,
  rootValue: productRoot,
};

2. ゲートウェイサービスの作成

次に、2つのスキーマをスティッチするゲートウェイサービスを作成します。

ゲートウェイサービス (gateway.js):


const { stitchSchemas } = require('@graphql-tools/stitch');
const { makeRemoteExecutableSchema } = require('@graphql-tools/wrap');
const { graphqlHTTP } = require('express-graphql');
const express = require('express');
const { introspectSchema } = require('@graphql-tools/wrap');
const { printSchema } = require('graphql');
const fetch = require('node-fetch');

async function createRemoteSchema(uri) {
  const fetcher = async (params) => {
    const response = await fetch(uri, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(params),
    });
    return response.json();
  };

  const schema = await introspectSchema(fetcher);
  return makeRemoteExecutableSchema({
    schema,
    fetcher,
  });
}

async function main() {
  const userSchema = await createRemoteSchema('http://localhost:4001/graphql');
  const productSchema = await createRemoteSchema('http://localhost:4002/graphql');

  const stitchedSchema = stitchSchemas({
    subschemas: [
      { schema: userSchema },
      { schema: productSchema },
    ],
    typeDefs: `
      extend type Product {
        user: User
      }
    `,
    resolvers: {
      Product: {
        user: {
          selectionSet: `{ userId }`,
          resolve(product, args, context, info) {
            return info.mergeInfo.delegateToSchema({
              schema: userSchema,
              operation: 'query',
              fieldName: 'user',
              args: {
                id: product.userId,
              },
              context,
              info,
            });
          },
        },
      },
    },
  });

  const app = express();
  app.use('/graphql', graphqlHTTP({
    schema: stitchedSchema,
    graphiql: true,
  }));

  app.listen(4000, () => console.log('Gateway server running on http://localhost:4000/graphql'));
}

main().catch(console.error);

3. サービスの実行

ユーザーサービスと製品サービスを異なるポートで実行する必要があります。例えば:

ユーザーサービス(ポート4001):


const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { schema, rootValue } = require('./user-service');

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: rootValue,
  graphiql: true,
}));

app.listen(4001, () => console.log('User service running on http://localhost:4001/graphql'));

製品サービス(ポート4002):


const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { schema, rootValue } = require('./product-service');

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: rootValue,
  graphiql: true,
}));

app.listen(4002, () => console.log('Product service running on http://localhost:4002/graphql'));

4. スティッチされたスキーマへのクエリ

これで、ゲートウェイ(ポート4000で実行中)を介してスティッチされたスキーマにクエリを実行できます。次のようなクエリを実行できます:


query {
  product(id: "101") {
    id
    name
    price
    user {
      id
      name
      email
    }
  }
}

このクエリはID「101」の製品を取得し、関連するユーザーをユーザーサービスからフェッチします。これは、スキーマスティッチングが単一のリクエストで複数のサービスにまたがるデータをクエリできることを示しています。

高度なスキーマスティッチング技術

基本的な例を超えて、スキーマスティッチングの実装を強化するために使用できるいくつかの高度な技術を紹介します:

スキーマスティッチングとApollo Federationの選択

スキーマスティッチングはGraphQLフェデレーションの実行可能な選択肢ですが、Apollo Federationはその高度な機能と改善された開発者体験により、より人気のある選択肢となっています。以下に2つのアプローチの比較を示します:

機能 スキーマスティッチング Apollo Federation
スキーマ定義 既存のGraphQLスキーマ言語を使用 ディレクティブを持つ宣言的なスキーマ言語を使用
クエリ計画 手動のクエリ委任が必要 Apollo Gatewayによる自動クエリ計画
型の拡張 限定的なサポート 型の拡張を組み込みでサポート
キードレクティブ サポートされていない @keyディレクティブを使用してエンティティを識別
分散トレーシング 手動での実装が必要 分散トレーシングを組み込みでサポート
ツールとエコシステム ツールが未成熟 より成熟したツールと大規模なコミュニティ
複雑さ 大規模システムでは管理が複雑になることがある 大規模で複雑なシステム向けに設計

スキーマスティッチングを選択する場合:

Apollo Federationを選択する場合:

実世界の例とユースケース

以下に、スキーマスティッチングを含むGraphQLフェデレーションがどのように使用できるかの実世界の例をいくつか示します:

スキーマスティッチングのベストプラクティス

スキーマスティッチングの実装を成功させるために、以下のベストプラクティスに従ってください:

結論

スキーマスティッチングによるGraphQLフェデレーションは、マイクロサービスアーキテクチャにおいて複数のサービスから統一されたAPIを構築するための強力なアプローチを提供します。その中心的な概念、利点、制限、および実装技術を理解することで、スキーマスティッチングを活用してデータアクセスを簡素化し、スケーラビリティを向上させ、保守性を高めることができます。Apollo Federationがより高度なソリューションとして登場していますが、スキーマスティッチングは、より単純なシナリオや既存のGraphQLサービスを統合する際には依然として実行可能な選択肢です。組織の特定のニーズと要件を慎重に検討し、最適なアプローチを選択してください。