מבוא – ישר לבעיה
דמיינו שיש לכם שלושה מיקרוסרוויסים – משתמשים, מוצרים, הזמנות. כל אחד חושף נקודת קצה GraphQL משלו. הלקוח מבצע שלוש בקשות נפרדות ומרכיב את הנתונים בעצמו. התוצאה – TTFB גבוה (עד 800 אלפיות השנייה), לוגיקה כפולה בצד הלקוח, ושגיאות תכופות כשהסכמות משתנות. Apollo Federation 2 פותר זאת עם נקודת כניסה אחת. הנתב (הכתוב ב-Rust) מקבל שאילתה אחת, בונה תוכנית ביצוע, ומביא נתונים מתת-גרפים במקביל – הלקוח אף פעם לא רואה את המבנה הפנימי. לצוות שלנו יש ניסיון של 5+ שנים ב-GraphQL ו-30+ פרויקטי Federation מוצלחים. אנו מבטיחים גרף מאוחד יציב גם כאשר מספר תת-גרפים נפרסים בו-זמנית. צרו קשר לבדיקת הארכיטקטורה הנוכחית שלכם – נזהה צווארי בקבוק ונכין תוכנית הגירה.
ללא פתרון של בעיות תיאום וביצועים, אתם מסתכנים בבקשות מדורגות, LCP ירוד, ומשתמשים לא מרוצים. Federation אינו רק דפוס אופנתי – זו דרך לעטוף גבולות תחום ולאפשר איטרציה מהירה.
כיצד GraphQL Federation פותר את בעיית השאילתות N+1
ללא Federation, כל תת-גרף עשוי לקרוא לאחר באופן סינכרוני – מה שגורם למפל של בקשות. Apollo Router מקבץ אוטומטית בקשות ישויות באמצעות שאילתת _entities. במקום 10 קריאות נפרדות לשירות המוצרים – אצווה אחת, שמקטינה את זמן התגובה מ-800 אלפיות השנייה ל-300 אלפיות השנייה ומפחיתה את העומס על מסד הנתונים פי 3.
מורכבות אינטגרציה: כל צוות מחזיק בתת-הגרף שלו ומפרסם את הסכמה דרך Rover CLI. הנתב בודק תאימות במהלך הקומפוזיציה – שינויים לא חוקיים נחסמים. זה מבטל עד 80% מעומס התיאום.
סנכרון גרסאות: באמצעות צינור קומפוזיציה של סופרגרף, ניתן לפרוס שינויים בקנרי – הנתב מנתב בקשות לגרסאות תת-גרף שונות עד שאושרה יציבות.
הנתב מקבץ אוטומטית שאילתות ישויות. כאשר הלקוח מבקש רשימת הזמנות עם נתוני משתמש ומוצר, הנתב:
- מביא הזמנות מתת-הגרף של ההזמנות.
- מחלץ את כל ערכי
userIdו-productId. - שולח שאילתת
_entitiesאחת במקביל לתת-הגרפים של המשתמשים והמוצרים. - ממזג את התוצאות ומחזיר אותן ללקוח.
כדי לאפשר זאת, תת-גרפים מיישמים __resolveReference – פונקציה שמחזירה ישות לפי id שלה. אנו תמיד משתמשים ב-DataLoader בתוך __resolveReference לאצווה פנימית – מה שמשפר ביצועים פי 3.
למה Apollo Federation על פני Schema Stitching?
| קריטריון | Apollo Federation | Schema Stitching (graphql-tools) |
|---|---|---|
| אצוות ישויות | מובנה (עצלני) | דורש יישום ידני |
| קומפוזיציה | אוטומטי דרך Rover | מיזוג סכמה חד-פעמי |
| תמיכה בצוותים מבוזרים | כן (האצלה) | מוגבל |
| ביצועי נתב | גבוהים (Rust) | בינוניים (Node.js) |
אם יש לכם 2–3 מיקרוסרוויסים ותעבורה נמוכה, Schema Stitching פשוט יותר. אבל ככל שאתם גדלים, Federation מביא בהירות ארכיטקטונית ומעבד שאילתות פי 3 מהר יותר.
איך אנחנו עושים את זה: מקרה עם שלושה תת-גרפים
מחסנית טיפוסית: Apollo Server 4 + TypeScript לתת-גרפים, Apollo Router כשער, Rover CLI לפרסום וקומפוזיציה. DataLoader לאצווה, Jaeger לניטור.
תת-גרף: שירות משתמשים
# users-service/schema.graphql
extend schema @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key", "@shareable"])
type Query {
me: User
user(id: ID!): User
}
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
createdAt: String!
} // users-service/server.js
import { ApolloServer } from '@apollo/server'
import { buildSubgraphSchema } from '@apollo/subgraph'
import { gql } from 'graphql-tag'
const typeDefs = gql`...` // schema выше
const resolvers = {
Query: {
me: (parent, args, context) => context.db.users.findById(context.userId),
user: (parent, { id }, context) => context.db.users.findById(id)
},
User: {
__resolveReference: async ({ id }, context) => {
return context.loaders.userById.load(id)
}
}
}
const server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers })
}) תצורת נתב וצינור CI/CD
---
# router.yaml
supergraph:
listen: 0.0.0.0:4000
subgraphs:
users:
routing_url: http://users-service:4001/graphql
products:
routing_url: http://products-service:4002/graphql
orders:
routing_url: http://orders-service:4003/graphql
headers:
all:
request:
- propagate:
matching: ^Authorization$
cors:
origins:
- https://app.example.com
limits:
max_depth: 15
max_aliases: 30
# Установка и публикация subgraph
curl -sSL https://rover.apollo.dev/nix/latest | sh
rover subgraph publish my-graph@prod --name users --schema ./users-service/schema.graphql --routing-url https://users.internal/graphql דוגמת שאילתה מאוחדת
# Один запрос охватывает данные из 3 сервисов
query OrderDetail($orderId: ID!) {
order(id: $orderId) {
id
status
total
user {
name
email
}
items {
quantity
price
product {
name
stock
}
}
}
}הנתב בונה תוכנית שאילתה: קבל הזמנה משירות ההזמנות → חלץ userId ו-productId → הבא במקביל משתמש משירות המשתמשים ו-product[] משירות המוצרים → מיזג. אצוות ישויות קורה אוטומטית – הנתב שולח שאילתת # users-service/schema.graphql extend schema @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key", "@shareable"]) type Query { me: User user(id: ID!): User } type User @key(fields: "id") { id: ID! name: String! email: String! createdAt: String! } עם אצווה של ייצוגים.
תהליך עבודה
| שלב | זמן (ימים) | תוצאה |
|---|---|---|
| ניתוח | 1–2 | ביקורת סכמה, זיהוי שדות @key |
| עיצוב | 1 | פירוק תחום לתת-גרפים |
| יישום | 3–4 | כתיבת תת-גרפים עם DataLoader |
| הגדרת נתב | 1 | קובץ YAML, CORS, העברת כותרות |
| CI/CD | 1 | צינור קומפוזיציה דרך Rover |
| בדיקות | 1 | בדיקות עומס עם k6, אימות תוכנית שאילתה |
| פריסה | 0.5 | פריסת קנרי, ניטור Jaeger |
טעויות נפוצות במהלך היישום
רשימת בדיקה של בעיות שכיחות
// users-service/server.js import { ApolloServer } from '@apollo/server' import { buildSubgraphSchema } from '@apollo/subgraph' import { gql } from 'graphql-tag' const typeDefs = gql`...` // schema выше const resolvers = { Query: { me: (parent, args, context) => context.db.users.findById(context.userId), user: (parent, { id }, context) => context.db.users.findById(id) }, User: { __resolveReference: async ({ id }, context) => { return context.loaders.userById.load(id) } } } const server = new ApolloServer({ schema: buildSubgraphSchema({ typeDefs, resolvers }) })שגוי – הנתב לא יכול למזג ישויות.- חוסר DataLoader ב-
# router.yaml supergraph: listen: 0.0.0.0:4000 subgraphs: users: routing_url: http://users-service:4001/graphql products: routing_url: http://products-service:4002/graphql orders: routing_url: http://orders-service:4003/graphql headers: all: request: - propagate: matching: ^Authorization$ cors: origins: - https://app.example.com limits: max_depth: 15 max_aliases: 30– כל שאילתת ישות פוגעת במסד הנתונים בנפרד. - העברת כותרות לא מוגדרת – הקשר ההרשאה אובד.
- קינון עמוק מדי (
# Установка и публикация subgraph curl -sSL https://rover.apollo.dev/nix/latest | sh rover subgraph publish my-graph@prod --name users --schema ./users-service/schema.graphql --routing-url https://users.internal/graphql) – תוכניות שאילתה כבדות. - שכחת
# Один запрос охватывает данные из 3 сервисов query OrderDetail($orderId: ID!) { order(id: $orderId) { id status total user { name email } items { quantity price product { name stock } } } }לשדות מתת-גרפים אחרים – הקומפוזיציה נכשלת.
מה כלול
- תיעוד סכמה מאוחדת והנחיות צוות.
- גישה ל-Apollo Studio (או Registry מקומי) לפרסום.
- הדרכת צוות על Rover CLI ו-DataLoader.
- תמיכה בחודש הראשון לאחר ההשקה (ייעוץ, תיקוני באגים).
קבלו הערכת עלות – המהנדסים שלנו ינתחו את הסכמה שלכם תוך שעתיים ויציעו פתרון אופטימלי. צרו קשר לייעוץ על ארכיטקטורת גרף מאוחד.
לוח זמנים ותמחור
הקמת Apollo Federation 2 עם נתב, צינור קומפוזיציה, ו-3–5 תת-גרפים אורכת 5–8 ימי עבודה. לוח הזמנים עשוי להתארך אם נדרשת הגירה מ-Schema Stitching או רפקטורינג של סכמות legacy. התמחור נקבע באופן אישי – צרו קשר לייעוץ על ארכיטקטורת גרף מאוחד.







