אפליקציית מובייל מבצעת 5–10 בקשות HTTP רצופות כדי לטעון עמוד בודד. ברשת סלולרית, כל סבב עגול מוסיף 200–500 אלפיות שנייה — ובסך הכול עד 2–3 שניות. תרחיש טיפוסי: /users/{id}, /orders?userId={id}&limit=3, /notifications/unread, /recommendations?userId={id}, /loyalty/points/{id}. הלקוח חייב להמתין לכל התשובות ולבצע טרנספורמציות. זה לא רק איטי אלא גם צורך תעבורה עודפת: טעינת שדות מיותרים (לדוגמה, כתובת מלאה בעת צפייה בעגלת קניות) מגדילה את נפח הנתונים המועברים ב-30–50%. אנו מציעים להכניס שכבת ביניים של Backend for Frontend (BFF). החיסכון בתעבורה ובתשתית יכול להגיע ל-40% לאחר יישום BFF, עם חיסכון טיפוסי של $2,000 לחודש בעלויות ענן.
BFF הוא שירות backend נפרד שנוצר עבור סוג לקוח ספציפי (web, אפליקציית מובייל, Smart TV). הוא מקבל בקשה אחת מהלקוח, שולח במקביל שאילתות למיקרוסרוויסים הדרושים, מאגד נתונים, מסיר שדות מיותרים, ומחזיר תוצאה בפורמט אופטימלי עבור אותו לקוח. הרעיון מפורט במאמר של Martin Fowler המאמר המקורי.
אילו בעיות BFF פותר?
בקשות N+1 וזמן השהיה. תמונה טיפוסית: 5–10 קריאות רצופות עבור עמוד אחד. BFF מצמצם זאת לקריאה אחת על ידי ביצוע בקשות מקבילות לשירותים. חיסכון בזמן — עד 80% מהסבבים העגולים (פי 4 מהר יותר מקריאות ישירות של הלקוח).
נתונים עודפים. מיקרוסרוויס מחזיר שדות שהלקוח לא צריך (לדוגמה, כתובת בעת צפייה בעגלת קניות). BFF מסיר נתונים מיותרים, ומפחית את נפח הנתונים המועברים ב-30–50% (לדוגמה, מ-150 KB ל-80 KB למסך).
אימות מורכב. JWT, refresh token, httpOnly cookie — BFF מנהל סשנים באופן מרכזי מבלי לחשוף שירותים מהלקוח.
כשלים חלקיים. אם שירות אחד אינו זמין, BFF יכול להחזיר תשובה חלקית (null עבור הקטע שנכשל) במקום שגיאת 500, תוך שימוש בלוגיקת retry עם exponential backoff.
שמירת נתונים נפוצים במטמון. הצבת BFF בין הלקוח לשירותים מאפשרת שמירת תשובות מאוגדות במטמון (לדוגמה, המלצות), מה שמפחית את העומס על מיקרוסרוויסים ב-40%.
כיצד אנו מיישמים BFF: טכנולוגיות ודוגמאות
בפועל, נוח לבנות BFF על Node.js עם Express או Apollo Server (GraphQL). בפרויקטים שלנו אנו משתמשים ב-Node.js, TypeScript, Express או Apollo Server, Redis לשמירה במטמון (ioredis), ו-axios עבור בקשות HTTP.
דוגמה לאפליקציית מובייל
נדרש endpoint אחד /mobile/dashboard, שמחזיר פרופיל, הזמנות אחרונות, התראות ונקודות נאמנות.
// mobile-bff/routes/dashboard.ts router.get('/mobile/dashboard', authenticate, async (req, res) => { const userId = req.user.id; const [userResult, ordersResult, notificationsResult, loyaltyResult] = await Promise.allSettled([ userService.get(`/users/${userId}`), orderService.get(`/orders?customerId=${userId}&limit=3&fields=id,status,total,createdAt`), notificationService.get(`/notifications/${userId}/unread-count`), loyaltyService.get(`/loyalty/${userId}/summary`) ]); const response = { user: userResult.status === 'fulfilled' ? { id: userResult.value.data.id, name: userResult.value.data.displayName, avatar: userResult.value.data.avatarUrl } : null, recentOrders: ordersResult.status === 'fulfilled' ? ordersResult.value.data.items.map(transformOrderForMobile) : [], unreadCount: notificationsResult.status === 'fulfilled' ? notificationsResult.value.data.count : 0, loyalty: loyaltyResult.status === 'fulfilled' ? { points: loyaltyResult.value.data.balance, tier: loyaltyResult.value.data.tier } : null }; res.json(response); }); דוגמה ללקוח Web
לקוח web זקוק לאותם נתונים אך עם פרופיל מלא, רשימת הזמנות גדולה יותר וניתוחים.
// web-bff/routes/dashboard.ts router.get('/web/dashboard', authenticate, async (req, res) => { const userId = req.user.id; const [user, orders, stats, notifications] = await Promise.allSettled([ userService.get(`/users/${userId}`), orderService.get(`/orders?customerId=${userId}&limit=10`), analyticsService.get(`/analytics/user/${userId}/stats`), notificationService.get(`/notifications/${userId}?limit=5&unread=true`) ]); res.json({ user: user.status === 'fulfilled' ? user.value.data : null, orders: orders.status === 'fulfilled' ? orders.value.data : { items: [], total: 0 }, analytics: stats.status === 'fulfilled' ? stats.value.data : null, notifications: notifications.status === 'fulfilled' ? notifications.value.data : [] }); }); הרחב לדוגמה של GraphQL BFF
// web-bff/graphql/schema.ts const typeDefs = gql` type Query { dashboard: Dashboard! order(id: ID!): Order } type Dashboard { user: User! recentOrders: [Order!]! stats: UserStats! } `; const resolvers = { Query: { dashboard: async (_, __, { userId }) => { const [user, orders, stats] = await Promise.all([ userService.getUser(userId), orderService.getRecentOrders(userId), analyticsService.getUserStats(userId) ]); return { user, recentOrders: orders, stats }; } } }; כיצד ליישם אימות ושמירה במטמון ב-BFF?
BFF הוא המקום הטבעי לאימות מרכזי ושמירה במטמון. אסימון JWT מאומת ברמת BFF, refresh token נשמר ב-httpOnly cookie (עבור לקוחות web), ומטמון Redis מאיץ תשובות עבור נתונים שמשתנים לעיתים רחוקות.
// authorization middleware router.post('/auth/refresh', async (req, res) => { const refreshToken = req.cookies.refresh_token; if (!refreshToken) return res.status(401).json({ error: 'No token' }); const tokens = await authService.refreshTokens(refreshToken); res.cookie('refresh_token', tokens.refreshToken, { httpOnly: true, secure: true, sameSite: 'strict', maxAge: 30 * 24 * 60 * 60 * 1000 }); res.json({ accessToken: tokens.accessToken }); }); // caching with Redis import Redis from 'ioredis'; const redis = new Redis(process.env.REDIS_URL); async function getCachedOrFetch<T>(key: string, ttl: number, fetcher: () => Promise<T>): Promise<T> { const cached = await redis.get(key); if (cached) return JSON.parse(cached); const data = await fetcher(); await redis.setex(key, ttl, JSON.stringify(data)); return data; } בחירה בין GraphQL ל-REST עבור BFF
אם הלקוח הוא אפליקציית React עם Apollo Client, BFF יכול לחשוף סכמת GraphQL. זה מאפשר ללקוח לבקש בדיוק את השדות הדרושים מבלי לשכפל endpoints. GraphQL BFF שימושי במיוחד כאשר דרישות הנתונים משתנות לעיתים קרובות: הלקוח שולט בשליפת הנתונים. עם זאת, עבור אפליקציות מובייל פשוטות עם מסכים קבועים, REST BFF לרוב יעיל יותר בשל מורכבות נמוכה יותר.
מה כלול בעבודה
- ביקורת על הארכיטקטורה הנוכחית וזיהוי סבבים עגולים תכופים.
- עיצוב שכבת BFF עבור כל לקוח (REST או GraphQL).
- פיתוח APIs עם איגוד נתונים וטרנספורמציה.
- יישום אימות (JWT, מבוסס סשן).
- שמירה במטמון עם Redis או בזיכרון.
- טיפול בשגיאות וכשלים חלקיים (Promise.allSettled).
- תיעוד (OpenAPI או GraphQL SDL).
- בדיקות ובדיקות עומס.
- פריסה (Docker, Nginx, Vercel, AWS Lambda).
לוחות זמנים
| סוג BFF | לוח זמנים | טווח מחירים |
|---|---|---|
| REST BFF עבור לקוח אחד (3–5 endpoints) | 1–2 שבועות | $5,000 – $10,000 |
| GraphQL BFF + אימות + שמירה במטמון | 2–3 שבועות | $10,000 – $15,000 |
| BFF עבור 2–3 לקוחות עם ספרייה משותפת | 3–4 שבועות | $15,000 – $20,000 |
תהליך העבודה
- ניתוח – לימוד דפוס הבקשות וזיהוי צווארי בקבוק.
- עיצוב – הגדרת קבוצת ה-endpoints המאוגדים והטכנולוגיות.
- יישום – כתיבת שכבת BFF, שמירה במטמון ואימות.
- בדיקות – בדיקות יחידה, אינטגרציה וביצועים.
- פריסה – פריסה והגדרת ניטור.
טעויות טיפוסיות ביישום BFF
- שכפול לוגיקה – כל BFF משכפל טרנספורמציות. פתרון: חילוץ פונקציות משותפות לספרייה משותפת.
- BFF מונוליטי – BFF אחד עבור כל הלקוחות מוביל לאותן בעיות כמו API אחיד. לכל לקוח צריך להיות BFF משלו.
-
התעלמות מכשלים חלקיים – אם שירות אחד נכשל, BFF לא צריך לקרוס. השתמש ב-
// mobile-bff/routes/dashboard.ts router.get('/mobile/dashboard', authenticate, async (req, res) => { const userId = req.user.id; const [userResult, ordersResult, notificationsResult, loyaltyResult] = await Promise.allSettled([ userService.get(`/users/${userId}`), orderService.get(`/orders?customerId=${userId}&limit=3&fields=id,status,total,createdAt`), notificationService.get(`/notifications/${userId}/unread-count`), loyaltyService.get(`/loyalty/${userId}/summary`) ]); const response = { user: userResult.status === 'fulfilled' ? { id: userResult.value.data.id, name: userResult.value.data.displayName, avatar: userResult.value.data.avatarUrl } : null, recentOrders: ordersResult.status === 'fulfilled' ? ordersResult.value.data.items.map(transformOrderForMobile) : [], unreadCount: notificationsResult.status === 'fulfilled' ? notificationsResult.value.data.count : 0, loyalty: loyaltyResult.status === 'fulfilled' ? { points: loyaltyResult.value.data.balance, tier: loyaltyResult.value.data.tier } : null }; res.json(response); });עם דפוס circuit breaker. - חוסר שמירה במטמון – ללא מטמון, כל בקשה פוגעת בשירותים, מה שמגדיל את העומס ואת זמן התגובה.
מדוע BFF עדיף על API אחיד?
ללא BFF, הלקוח חייב לאגד נתונים בעצמו, מה שמוביל לבקשות מיותרות וללוגיקת לקוח מסובכת. BFF מטפל באורכסטרציה: בקשה אחת מהלקוח → שאילתות מקבילות למיקרוסרוויסים → איגוד → טרנספורמציה → תשובה. זה מאיץ את הטעינה (פי 4 מהר יותר), מפחית תעבורה (46% פחות), ומפשט את התחזוקה.
הניסיון שלנו
יישמנו BFF עבור פרויקט מסחר אלקטרוני גדול. תוצאות:
| מדד | ללא BFF | עם BFF |
|---|---|---|
| מספר בקשות למסך | 7 | 1 |
| זמן טעינה | 3.2 שניות | 0.8 שניות |
| תעבורה למסך | 150 KB | 80 KB |
| עומס על שירותים | 100% | ~60% |
הניסיון של המהנדסים שלנו: 8+ שנים בארכיטקטורת מיקרוסרוויסים. הזמינו פיתוח BFF החל מ-$5,000 וקבלו ערבויות לזמן תגובה וסבילות לתקלות. צרו קשר לייעוץ חינם – נעריך את הפרויקט שלכם תוך 1–2 ימים. הזמינו פיתוח BFF וקבלו אופטימיזציה מותאמת ללקוח שלכם.







