خدمة الواجهة الخلفية للواجهة الأمامية (BFF) هي خدمة خلفية مخصصة لواجهة أمامية واحدة. بدل أن يتصل الويب و iOS و Android والتكاملات الخارجية بنفس API العام، تحصل كل تجربة عميل على طبقة خادم صغيرة تجمع البيانات من الخدمات المصغرة وتعيد تشكيلها بالشكل الذي تحتاجه تلك الواجهة فقط.
أطلق سام نيومان هذا النمط وروّج له في عام 2015 بناءً على تجربة SoundCloud. وما زال BFF نمطًا عمليًا للفرق التي تدير خدمات مصغرة خلف عدة تطبيقات عميل، كما توثقه مايكروسوفت كنمط معماري سحابي أساسي.
المشكلة التي تحلها خدمة BFF
ابدأ من سيناريو شائع:
- تطبيق ويب واحد.
- واجهة خلفية واحدة.
- عدة نقاط REST يستهلكها العميل.
ثم تضيف الشركة:
- تطبيقًا للجوال.
- تكاملًا مع شريك.
- تطبيقًا لساعة ذكية.
النتيجة: عملاء مختلفون جدًا يستخدمون نفس الواجهة الخلفية العامة. هنا تظهر مشكلتان.
1. الإفراط في الجلب والنقص في الجلب
نقطة نهاية عامة ترجع نفس شكل البيانات للجميع.
مثال:
- لوحة تحكم سطح المكتب تحتاج ملف العميل كاملًا، الطلبات، التوصيات، وإعدادات الحساب.
- تطبيق الجوال يحتاج الاسم، الصورة، وعدد الإشعارات فقط.
إذا استخدم الاثنان نفس endpoint:
GET /api/customer/123
فإما أن يحصل الجوال على حمولة كبيرة ويتجاهل معظمها، أو يضطر إلى طلب عدة endpoints صغيرة لبناء الشاشة.
2. العملاء الثرثارون
إذا كانت شاشة الجوال الرئيسية تحتاج:
- بيانات الملف الشخصي.
- عدد الإشعارات.
- موجز آخر النشاطات.
فقد يكتب العميل شيئًا مثل:
const [profile, notifications, feed] = await Promise.all([
fetch("/api/profile"),
fetch("/api/notifications/count"),
fetch("/api/feed"),
]);
هذا يعمل، لكنه ينقل منطق التجميع إلى الجهاز، ويزيد عدد الرحلات الشبكية، ويجعل الاختبار والتطوير أصعب.
المشكلة ليست تقنية فقط. الواجهة الخلفية المشتركة تصبح نقطة تفاوض بين فرق الويب والجوال والشركاء. أي تغيير صغير يجب أن يراعي الجميع، فتتحول API العامة إلى عنق زجاجة.
كيف يعمل نمط BFF
يضيف نمط BFF طبقة خادم رفيعة بين كل واجهة أمامية والخدمات النهائية:
[ Web app ] ---> [ Web BFF ] ---\
[ iOS app ] ---> [ iOS BFF ] -----> [ Microservices ]
[ Android app ] ---> [ Android BFF ] ---/
كل BFF يخدم تجربة عميل واحدة، ويقوم عادة بثلاث مهام.
1. التجميع
بدل أن يطلب العميل خمس خدمات، يطلب endpoint واحدًا من BFF:
GET /mobile/home
ثم يقوم BFF داخليًا باستدعاء الخدمات اللازمة:
app.get("/mobile/home", async (req, res) => {
const userId = req.user.id;
const [profile, notifications, feed] = await Promise.all([
profileService.getProfile(userId),
notificationService.getUnreadCount(userId),
feedService.getLatest(userId),
]);
res.json({
user: {
name: profile.name,
avatarUrl: profile.avatarUrl,
},
unreadNotifications: notifications.count,
feed: feed.items.slice(0, 10),
});
});
هذا هو تجميع API على مستوى تجربة مستخدم واحدة. إذا أردت النسخة العامة من الفكرة، راجع شرح نمط مجمع واجهة برمجة التطبيقات API aggregator.
2. إعادة التشكيل
BFF يقص الحقول غير اللازمة، يعيد تسمية المفاتيح، يسطّح الهياكل المتداخلة، ويرجع حمولة مناسبة للواجهة.
مثال: خدمة داخلية ترجع:
{
"id": "u_123",
"personalInformation": {
"firstName": "Sara",
"lastName": "Ali"
},
"accountSettings": {
"marketingEmails": true
},
"internalRiskScore": 72
}
بينما BFF للجوال يرجع:
{
"id": "u_123",
"displayName": "Sara Ali"
}
3. الترجمة
يمكن لـ BFF التعامل مع تفاصيل خاصة بالعميل مثل:
- استراتيجية pagination.
- تخزين مؤقت مناسب للجوال.
- ترجمة بروتوكول داخلي إلى REST أو JSON أبسط.
- إخفاء تفاصيل الخدمات المصغرة عن الواجهة.
الخدمات المصغرة تبقى عامة ومستقلة عن العميل. أما منطق التشكيل الخاص بالواجهة فيبقى داخل BFF. إذا كنت تبني طبقة الخدمات الأساسية، فهذه المراجع تساعدك على وضع السياق: الخدمات المصغرة مقابل واجهات برمجة التطبيقات والانتقال من التطبيقات المتجانسة إلى الخدمات المصغرة.
خدمة BFF واحدة لكل تجربة عميل
القاعدة العملية من سام نيومان: تجربة واحدة، BFF واحد.
استخدم BFF منفصلًا عندما تكون التجارب مختلفة فعلًا:
- Web BFF للوحة تحكم غنية.
- Mobile BFF لتطبيق يحتاج حمولات صغيرة.
- Partner BFF لتكامل خارجي بعقد API مستقر ومحدود.
لا تجعل BFF واحدًا يخدم الجميع بهذا الشكل:
if (clientType === "mobile") {
return mobilePayload(data);
}
if (clientType === "web") {
return webPayload(data);
}
if (clientType === "partner") {
return partnerPayload(data);
}
عندما يمتلئ BFF بشروط حسب نوع العميل، فأنت تعيد بناء واجهة خلفية عامة جديدة.
الاستثناء المعقول: إذا كان فريق واحد يمتلك iOS و Android، والتجربتان متشابهتان جدًا، فقد يكفي Mobile BFF واحد. العامل الحاسم هو التشابه والملكية، وليس اسم المنصة.
الملكية تعود لفريق الواجهة الأمامية
BFF ليس طبقة يبنيها فريق المنصة ثم يسلّمها. الفريق الذي يمتلك الواجهة الأمامية يجب أن يمتلك BFF الخاص بها.
هذا يعني أن الفريق يستطيع:
- إصدار تغييرات الواجهة وBFF معًا.
- اختيار لغة وبيئة تشغيل مناسبة.
- إضافة endpoint جديد دون انتظار فريق خلفية منفصل.
- اختبار العقد الذي تعتمد عليه الواجهة مباشرة.
مثال عملي: إذا احتاجت شاشة جديدة endpoint مجمّعًا:
GET /mobile/orders/summary
ففريق الجوال يضيفه في Mobile BFF، بدل أن يطلب من فريق API العام تغيير endpoint يستخدمه الويب والشركاء أيضًا.
هذا يطابق فكرة طبقة "التجربة" في الاتصال الموجه بواجهة برمجة التطبيقات API-led connectivity.
BFF مقابل بوابة API
BFF وAPI Gateway يبدوان متشابهين لأن كليهما يقع بين العملاء والخدمات. لكنهما يحلان مشكلتين مختلفتين.
بوابة API تتعامل مع المخاوف المشتركة لكل المرور:
- المصادقة.
- تحديد المعدل.
- التوجيه.
- إنهاء TLS.
- تسجيل الطلبات.
- المراقبة.
أما BFF فيتعامل مع ما هو خاص بعميل واحد:
- شكل الحمولة.
- تجميع بيانات شاشة محددة.
- تقليل عدد طلبات العميل.
- ترجمة احتياجات الواجهة إلى استدعاءات خدمات داخلية.
تصميم شائع في الإنتاج:
[ Clients ]
|
[ API Gateway ]
|
+--> [ Web BFF ] --> [ Microservices ]
+--> [ Mobile BFF ] --> [ Microservices ]
+--> [ Partner BFF] --> [ Microservices ]
استخدم البوابة لما هو مشترك بين كل العملاء، واستخدم BFF لما يختلف بين تجربة وأخرى.
للمقارنة مع أنماط قريبة، راجع: إدارة API مقابل بوابة API، بوابة API مقابل موازن التحميل، وشبكة الخدمات مقابل بوابة API.
متى تستخدم خدمة BFF
استخدم BFF عندما تنطبق هذه الشروط.
لديك عملاء متعددون ومختلفون
مثال:
- الويب يحتاج بيانات كثيرة في شاشة واحدة.
- الجوال يحتاج استجابات صغيرة وسريعة.
- الشريك يحتاج عقد API ثابتًا ومحدودًا.
كلما اختلفت التجارب، زادت فائدة BFF.
الواجهة الخلفية المشتركة أصبحت عنق زجاجة
إذا كان كل تغيير في الواجهة يحتاج تنسيقًا مع عدة فرق، انقل منطق التشكيل الخاص بالعميل إلى BFF يملكه فريق الواجهة.
تريد تحسين الحمولة لكل عميل
الجوال قد يحتاج:
{
"title": "Order #123",
"status": "shipped"
}
بينما الويب يحتاج:
{
"id": "123",
"status": "shipped",
"items": [],
"customer": {},
"shippingAddress": {},
"payment": {},
"timeline": []
}
لا تجبرهما على نفس العقد إن كانت احتياجاتهما مختلفة.
تريد حرية تقنية لفريق الواجهة
يمكن لفريق الويب بناء Web BFF بـ Node.js، وفريق آخر استخدام .NET أو Java أو بيئة serverless، طالما أن العقود واضحة والخدمات الأساسية مستقرة.
متى لا تستخدم خدمة BFF
لا تضف BFF تلقائيًا. تجنبه في هذه الحالات.
لديك عميل واحد فقط
إذا كان لديك واجهة واحدة فقط، فـ BFF غالبًا مجرد قفزة إضافية. ابنِ backend عاديًا.
العملاء يطلبون نفس البيانات تقريبًا
إذا كان الويب والجوال يستخدمان نفس الحقول ونفس الشكل، فالفصل سيكرر العمل دون فائدة.
GraphQL يحل مشكلة التشكيل لديك
GraphQL يسمح لكل عميل بطلب الحقول التي يحتاجها من نقطة نهاية واحدة. إذا كانت لديك طبقة GraphQL مع resolvers مناسبة للواجهات، فقد لا تضيف طبقة BFF منفصلة قيمة كبيرة.
بوابة API مع خدمات مصغرة كافية
في الأنظمة الأبسط، قد تكون API Gateway أمام خدمات مصغرة مصممة جيدًا كافية دون طبقة مخصصة لكل عميل.
العيوب الصريحة
حتى عندما يكون BFF مناسبًا، له تكلفة.
تكرار الكود
قد تكرر عدة BFFs نفس المنطق:
- تنسيق التاريخ.
- تحويل الأخطاء.
- استدعاء تحقق مشترك.
الحل:
- ضع المنطق المشترك الحقيقي في مكتبات مشتركة.
- اترك BFF لمنطق التشكيل الخاص بالعميل.
- انقل المخاوف الشاملة مثل المصادقة وتحديد المعدل والمراقبة إلى API Gateway.
المزيد من الخدمات للتشغيل
كل BFF يحتاج:
- نشرًا.
- مراقبة.
- سجلات.
- اختبارات.
- إدارة أسرار.
- مسؤولية تشغيلية.
لا تضف BFF إذا لم تكن مستعدًا لتشغيله كخدمة إنتاجية.
قفزة شبكة إضافية
BFF يضيف hop بين العميل والخدمات. غالبًا يعوض ذلك بتقليل عدد طلبات العميل، لكن لا تفترض. قِس زمن الاستجابة قبل وبعد.
خطر تضخم BFF
BFF يجب أن يبقى خفيفًا. لا تضع فيه منطق أعمال عميقًا يخص الخدمات المصغرة.
قاعدة عملية:
- إذا كان المنطق يخص تجربة عميل: ضعه في BFF.
- إذا كان المنطق يخص قدرة أعمال عامة: ضعه في الخدمة المصغرة.
- إذا كان المنطق يخص كل المرور: ضعه في API Gateway أو طبقة مشتركة.
الحفاظ على مزامنة عقود BFF مع Apidog
أصعب جزء في تشغيل BFF عمليًا هو العقود.
كل BFF لديه عقد API موجه للعميل، وفي نفس الوقت يعتمد على عقود الخدمات المصغرة الأساسية. أي انحراف بين هذه العقود قد يكسر الواجهة.
هنا يمكن استخدام Apidog كجزء من سير العمل. Apidog منصة لتصميم API واختبارها ومحاكاتها وتوثيقها، بحيث يصبح عقد كل BFF في مكان واحد يمكن لفرق الواجهة والخلفية العمل عليه.
1. صمم العقد أولًا
ابدأ بتحديد endpoints الخاصة بـ BFF قبل التنفيذ:
GET /mobile/home
response:
200:
application/json:
user:
name: string
avatarUrl: string
unreadNotifications: number
feed:
- id: string
title: string
استخدم OpenAPI كقاعدة لتثبيت شكل الطلب والاستجابة قبل كتابة الكود. هذا هو نهج تصميم API على أساس العقد أولًا مطبقًا على طبقة BFF، مع الحفاظ على عقد API واضح.
2. حاكِ BFF قبل تنفيذه
بعد الاتفاق على العقد، يستطيع فريق الواجهة البدء بالعمل ضد mock API بدل انتظار تنفيذ BFF والخدمات النهائية.
مثال:
const response = await fetch("https://mock.example.com/mobile/home");
const home = await response.json();
هذا يقلل الحظر بين الفرق، لأن الواجهة يمكنها التطوير والاختبار مبكرًا.
3. اختبر العقد تلقائيًا
أضف اختبارات تؤكد أن BFF يرجع الشكل المتفق عليه:
{
"user": {
"name": "Sara Ali",
"avatarUrl": "https://example.com/avatar.png"
},
"unreadNotifications": 3,
"feed": []
}
الفكرة أن يفشل CI إذا تغيرت خدمة داخلية بطريقة تكسر استجابة BFF المتوقعة.
4. وثّق للجانبين
وثائق BFF يجب أن تخدم:
- فريق الواجهة الذي يستهلك API.
- فريق الخدمات الأساسية الذي يوفر البيانات.
- فريق الاختبار الذي يبني سيناريوهات التحقق.
Apidog يولد وثائق تفاعلية من العقد، ما يجعل العقد مصدرًا واحدًا للحقيقة.
توضيح مهم: Apidog لا يبني ولا يستضيف ولا يشغل BFF، وليس API Gateway. دوره هو تصميم ومحاكاة واختبار وتوثيق عقد API الذي تعتمد عليه خدمات BFF. التعامل مع كل BFF كمنتج بعقد مستقر وموثق هو ما يجعل النمط قابلًا للاستدامة.
قائمة تنفيذ سريعة
إذا أردت تطبيق BFF في مشروعك، ابدأ بهذه الخطوات:
- احصر العملاء الموجودين: Web، iOS، Android، شركاء.
- قارن احتياجات البيانات لكل شاشة رئيسية.
- أنشئ BFF فقط للتجارب التي تختلف فعليًا.
- اجعل فريق الواجهة مالكًا لـ BFF.
- ضع المصادقة وتحديد المعدل والمراقبة المشتركة في API Gateway.
- اكتب عقد OpenAPI لكل BFF قبل التنفيذ.
- وفّر mock API للواجهة مبكرًا.
- أضف اختبارات عقد في CI.
- راقب زمن الاستجابة وعدد الطلبات قبل وبعد BFF.
- راجع BFF دوريًا لمنع تحوله إلى backend عام جديد.
الأسئلة الشائعة
هل BFF خدمة مصغرة؟
نعم، غالبًا تعمل كخدمة مستقلة في بيئة خدمات مصغرة. لكنها ليست خدمة قدرة أعمال عامة. الخدمة المصغرة تمتلك قدرة مثل الطلبات أو الدفع أو المستخدمين، بينما BFF يمتلك تجربة عميل واحدة ويجمع ويعيد تشكيل البيانات لها.
كم عدد خدمات BFF التي يجب أن أمتلكها؟
الافتراضي: واحدة لكل تجربة عميل مميزة. مثلًا:
- Web BFF.
- Mobile BFF.
- Partner BFF.
ادمج فقط عندما تكون التجارب متشابهة جدًا ويمتلكها نفس الفريق. افصل عندما يبدأ الكود بالامتلاء بشروط حسب نوع العميل.
هل يحل GraphQL محل نمط BFF؟
أحيانًا. GraphQL يحل جزءًا كبيرًا من مشكلة تشكيل الحمولة لأنه يسمح للعميل بطلب الحقول المطلوبة فقط. لكن BFF يبقى مفيدًا إذا كنت تحتاج تنسيقًا خاصًا بالعميل، أو ترجمة بروتوكول، أو قرارات تشغيلية لا تناسب خادم GraphQL مشتركًا.
هل يمكن استخدام BFF وبوابة API معًا؟
نعم. هذا تصميم شائع. API Gateway تتعامل مع المخاوف المشتركة مثل المصادقة وتحديد المعدل والمراقبة، ثم توجه الطلب إلى BFF المناسب. BFF يتعامل مع شكل الحمولة وتجربة العميل.
من يجب أن يمتلك خدمة BFF؟
فريق الواجهة الأمامية الذي يمتلك العميل. هذه الملكية جزء أساسي من النمط لأنها تسمح للفريق بإصدار تغييرات الواجهة والـ API الخاصة بها معًا.
هل تضيف خدمة BFF زمن استجابة؟
نعم، تضيف قفزة شبكة. لكنها غالبًا تقلل زمن الاستجابة الكلي لأنها تستبدل عدة طلبات من العميل بطلب واحد إلى BFF، ثم يستدعي BFF الخدمات بالتوازي وبقربها داخل البنية التحتية. الأفضل دائمًا هو القياس على حملك الفعلي.

Top comments (0)