עברית

גלו את העוצמה של פדרציית GraphQL באמצעות איחוי סכמות. למדו כיצד לבנות API מאוחד של GraphQL ממספר שירותים, תוך שיפור הסקלביליות והתחזוקתיות.

פדרציית GraphQL: איחוי סכמות (Schema Stitching) - מדריך מקיף

בנוף המתפתח תמיד של פיתוח יישומים מודרני, הצורך בארכיטקטורות סקלביליות וקלות לתחזוקה הפך לחיוני. מיקרו-שירותים, עם המודולריות המובנית ויכולת הפריסה העצמאית שלהם, הופיעו כפתרון פופולרי. עם זאת, ניהול מספר רב של מיקרו-שירותים יכול להציג מורכבויות, במיוחד כשמדובר בחשיפת API מאוחד ליישומי לקוח. כאן נכנסת לתמונה פדרציית GraphQL, ובפרט, איחוי סכמות (Schema Stitching).

מהי פדרציית GraphQL?

פדרציית GraphQL היא ארכיטקטורה עוצמתית המאפשרת לכם לבנות API יחיד ומאוחד של GraphQL ממספר שירותי GraphQL בסיסיים (שלעיתים קרובות מייצגים מיקרו-שירותים). היא מאפשרת למפתחים לשאול נתונים על פני שירותים שונים כאילו היו גרף יחיד, מה שמפשט את חווית הלקוח ומפחית את הצורך בלוגיקת תזמור מורכבת בצד הלקוח.

קיימות שתי גישות עיקריות לפדרציית GraphQL:

מאמר זה מתמקד באיחוי סכמות (Schema Stitching), ובוחן את המושגים, היתרונות, המגבלות והיישום המעשי שלו.

הבנת איחוי סכמות (Schema Stitching)

איחוי סכמות הוא תהליך של מיזוג מספר סכמות GraphQL לסכמה אחת, קוהרנטית. סכמה מאוחדת זו פועלת כחזית (facade), המסתירה את מורכבות השירותים הבסיסיים מהלקוח. כאשר לקוח מגיש בקשה לסכמה המאוחדת, ה-gateway מנתב בצורה חכמה את הבקשה לשירות(ים) הבסיסי(ים) המתאימ(ים), מאחזר את הנתונים, ומשלב את התוצאות לפני החזרתן ללקוח.

חשבו על זה כך: יש לכם מספר מסעדות (שירותים), שכל אחת מהן מתמחה במטבחים שונים. איחוי סכמות הוא כמו תפריט אוניברסלי המשלב את כל המנות מכל מסעדה. כאשר לקוח (client) מזמין מהתפריט האוניברסלי, ההזמנה מנותבת בצורה חכמה למטבחי המסעדות המתאימות, האוכל מוכן, ולאחר מכן משולב למשלוח אחד עבור הלקוח.

מושגי מפתח באיחוי סכמות

היתרונות של איחוי סכמות

איחוי סכמות מציע מספר יתרונות משכנעים לארגונים המאמצים ארכיטקטורת מיקרו-שירותים:

מגבלות של איחוי סכמות

בעוד שאיחוי סכמות מציע יתרונות רבים, חשוב להיות מודעים למגבלותיו:

יישום מעשי של איחוי סכמות

בואו נעבור על דוגמה מפושטת לאופן יישום איחוי סכמות באמצעות Node.js וספריית graphql-tools (בחירה פופולרית לאיחוי סכמות). דוגמה זו כוללת שני מיקרו-שירותים: שירות משתמשים ושירות מוצרים.

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!  # Foreign key to User Service
  }

  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. יצירת שירות ה-Gateway

כעת, ניצור את שירות ה-gateway שיאחה את שתי הסכמות יחד.

שירות ה-Gateway (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. שליחת שאילתה לסכמה המאוחדת

כעת אתם יכולים לשלוח שאילתה לסכמה המאוחדת דרך ה-gateway (הרצה על פורט 4000). תוכלו להריץ שאילתה כזו:


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

שאילתה זו מאחזרת את המוצר עם מזהה "101" וגם מביאה את המשתמש המשויך משירות המשתמשים, מה שמדגים כיצד איחוי סכמות מאפשר לכם לשאול נתונים על פני מספר שירותים בבקשה אחת.

טכניקות מתקדמות לאיחוי סכמות

מעבר לדוגמה הבסיסית, הנה כמה טכניקות מתקדמות שניתן להשתמש בהן כדי לשפר את יישום איחוי הסכמות שלכם:

בחירה בין איחוי סכמות לפדרציית אפולו

בעוד שאיחוי סכמות הוא אפשרות בת-קיימא לפדרציית GraphQL, פדרציית אפולו הפכה לבחירה הפופולרית יותר בזכות התכונות המתקדמות וחווית המפתח המשופרת שלה. הנה השוואה בין שתי הגישות:

תכונה איחוי סכמות (Schema Stitching) פדרציית אפולו (Apollo Federation)
הגדרת סכמה משתמשת בשפת הסכמה הקיימת של GraphQL משתמשת בשפת סכמה הצהרתית עם הנחיות (directives)
תכנון שאילתות דורשת האצלת שאילתות ידנית תכנון שאילתות אוטומטי על ידי ה-Apollo Gateway
הרחבות טיפוסים תמיכה מוגבלת תמיכה מובנית להרחבות טיפוסים
הנחיות מפתח (Key Directives) לא נתמך משתמשת בהנחיית @key לזיהוי ישויות
מעקב מבוזר דורש יישום ידני תמיכה מובנית למעקב מבוזר
כלים ואקוסיסטם כלים פחות בשלים כלים בשלים יותר וקהילה גדולה
מורכבות יכול להיות מורכב לניהול במערכות גדולות מיועד למערכות גדולות ומורכבות

מתי לבחור באיחוי סכמות:

מתי לבחור בפדרציית אפולו:

דוגמאות מהעולם האמיתי ומקרי שימוש

הנה כמה דוגמאות מהעולם האמיתי לאופן שבו ניתן להשתמש בפדרציית GraphQL, כולל איחוי סכמות:

שיטות עבודה מומלצות לאיחוי סכמות

כדי להבטיח יישום מוצלח של איחוי סכמות, עקבו אחר שיטות העבודה המומלצות הבאות:

סיכום

פדרציית GraphQL עם איחוי סכמות מציעה גישה עוצמתית לבניית ממשקי API מאוחדים ממספר שירותים בארכיטקטורת מיקרו-שירותים. על ידי הבנת מושגי הליבה, היתרונות, המגבלות וטכניקות היישום שלה, תוכלו למנף את איחוי הסכמות כדי לפשט את הגישה לנתונים, לשפר את הסקלביליות ולהגביר את התחזוקתיות. בעוד שפדרציית אפולו הופיעה כפתרון מתקדם יותר, איחוי סכמות נותר אפשרות בת-קיימא לתרחישים פשוטים יותר או בעת שילוב שירותי GraphQL קיימים. שקלו בקפידה את הצרכים והדרישות הספציפיות שלכם כדי לבחור את הגישה הטובה ביותר לארגון שלכם.