نمط Strangler Fig: الترحيل التدريجي
في درس 'مشروع: تحويل Monolith إلى Microservices' بنينا مثالاً عملياً على الهجرة، لكن دون تسمية النمط المعماري خلفه رسمياً: Strangler Fig (نسبة لنبتة التين الخانق التي تنمو حول شجرة مضيفة وتحل محلها تدريجياً دون أن تسقط الشجرة الأصلية فجأة).
الفكرة: بدل إعادة كتابة النظام القديم بالكامل دفعة واحدة (Big Bang Rewrite) — وهو مسار عالي المخاطر يفشل غالباً — تضع واجهة (Façade) أمام النظام القديم، وتنقل الوظائف واحدة تلو الأخرى لخدمات جديدة، بينما الواجهة توجّه كل طلب إما للنظام القديم أو للخدمة الجديدة، دون أن يلاحظ المستخدم أي فرق.
خطوات Strangler Fig:
1. ضع Façade (غالباً API Gateway) أمام الـ Monolith بالكامل
2. اختر وظيفة واحدة محدودة (مثال: كتالوج المنتجات) وابنِها كخدمة مستقلة
3. وجّه الـ Façade لطلبات هذه الوظيفة تحديداً للخدمة الجديدة، والباقي يبقى للـ Monolith
4. كرّر مع الوظيفة التالية حتى يتقلّص الـ Monolith تدريجياً
5. عندما لا يبقى شيء يستخدمه — أزِله
// الـ Façade يقرر الوجهة حسب المسار — تدريجياً أكثر المسارات تنتقل لليمين
app.use('/api/products', (req, res) => proxyTo('product-service', req, res)); // ✅ رُحّلت
app.use('/api/orders', (req, res) => proxyTo('order-service', req, res)); // ✅ رُحّلت
app.use('/api', (req, res) => proxyTo('legacy-monolith', req, res)); // بقية الوظائف — لم تُرحّل بعد
💡 الميزة الأساسية: كل خطوة صغيرة وقابلة للتراجع. لو ظهرت مشكلة في خدمة جديدة، تعيد توجيه حركتها للـ Monolith القديم مؤقتاً دون توقف كامل للنظام.
⚠️ التحدي الأكبر ليس نقل الكود، بل البيانات المشتركة: خلال فترة الهجرة، قد يحتاج كل من النظام القديم والخدمة الجديدة الوصول لنفس البيانات — يجب التخطيط لذلك (قاعدة بيانات مشتركة مؤقتة، أو مزامنة عبر أحداث) قبل البدء.
نمط Backends for Frontends (BFF)
نتعلّم في درس بوابة API أن API Gateway نقطة دخول موحدة. لكن مع نمو النظام، عملاء مختلفين (تطبيق جوال، واجهة ويب، شركاء خارجيون) يحتاجون بيانات مختلفة الشكل من نفس الخدمات: التطبيق الجوال يريد استجابة مضغوطة موفّرة للبيانات، والويب يريد تفاصيل أوسع لعرضها دفعة واحدة.
بدل بوابة واحدة تحاول إرضاء الجميع بمنطق شرطي متضخّم، BFF يخصّص بوابة منفصلة لكل نوع عميل، كل واحدة مصمّمة خصيصاً لاحتياجاته.
# بدل بوابة عامة واحدة تخدم الجميع
Single API Gateway:
mobile-app → /api/* (نفس الاستجابة الكاملة للجميع)
web-app → /api/*
partner-api → /api/*
# ✅ BFF: بوابة مخصصة لكل نوع عميل
Backends for Frontends:
mobile-bff → استجابات مختصرة، حقول أقل، طلبات أقل عبر الشبكة الخلوية
web-bff → استجابات أوسع، تجميع بيانات لصفحة كاملة بطلب واحد
partner-bff → عقد API مستقر ومختلف، غير مرتبط بتغييرات واجهة الويب الداخلية
⚠️ التكلفة: كل BFF خدمة إضافية يجب تطويرها ونشرها ومراقبتها. لا تنشئ BFF منفصلاً إلا حين تتعارض فعلاً احتياجات العملاء المختلفين — تطبيقان يعرضان نفس البيانات بنفس الشكل لا يحتاجان BFF منفصلاً.
نمط API Composition: تجميع الاستجابات
مشكلة أخرى شائعة: كل خدمة لها قاعدة بياناتها الخاصة (كما رأينا في درس إدارة الحالة الموزعة)، فكيف تعرض للمستخدم "صفحة ملخص الطلب" التي تحتاج بيانات من خدمة الطلبات وخدمة الشحن وخدمة العملاء معاً؟
API Composition يحل هذا عبر مكوّن (غالباً في الـ Gateway أو BFF نفسه) يستدعي كل خدمة على حدة، ثم يدمج (Join) النتائج في الذاكرة قبل إرجاعها للعميل كاستجابة واحدة.
// مكوّن API Composer — يجمع من ثلاث خدمات لاستجابة واحدة
async function getOrderSummary(orderId: string) {
const [order, shipment, customer] = await Promise.all([
orderServiceClient.getOrder(orderId),
shippingServiceClient.getShipment(orderId),
customerServiceClient.getCustomer(order.customerId),
]);
return {
orderId: order.id,
status: order.status,
total: order.total,
shippingStatus: shipment.status,
customerName: customer.name,
};
}
💡 هذا بالضبط ما يفعله BFF عملياً في أغلب الحالات: BFF هو المكان الطبيعي لتطبيق API Composition، لأنه أقرب نقطة تعرف احتياجات كل نوع عميل بالضبط.
⚠️ API Composition يناسب التجميع البسيط لطلب واحد. لو احتجت استعلامات معقّدة متكررة عبر بيانات عدة خدمات (تقارير، بحث)، النمط الأنسب هو CQRS مع نموذج قراءة مُجمَّع مسبقاً — كما رأينا في درس نمط CQRS — بدل استدعاء كل الخدمات في كل طلب.
أشهر الأخطاء
- محاولة "Big Bang Rewrite" بدل الترحيل التدريجي — مخاطرة عالية غالباً ما تفشل في مشاريع كبيرة
- إبقاء الـ Façade نقطة فشل واحدة (Single Point of Failure) دون تكرار (Replication) لها أثناء فترة الهجرة الطويلة
- إنشاء BFF منفصل لكل عميل حتى لو كانت احتياجاتهم متطابقة تقريباً — تعقيد تشغيلي بلا فائدة حقيقية
🎯 التالي: ملخص مسار المايكروسيرفيس