تطبيقنا المونوليث كان وحشاً لا يمكن المساس به: كيف أنقذنا “نمط التين الخانق” من جحيم التحديث المستحيل؟

يا أهلاً وسهلاً فيكم، معكم أخوكم أبو عمر.

خلوني أحكي لكم قصة صارت معي قبل كم سنة، قصة بتجسد معنى “الورطة التقنية”. كان عنا في الشركة تطبيق ضخم، هو العمود الفقري لكل شغلنا. كنا نسميه بين بعض “الختيار”، لأنه قديم، عنيد، ومزاجه صعب. هذا “الختيار” كان تطبيقاً متراصاً (Monolith)، يعني كل شيء فيه قطعة واحدة: واجهة المستخدم، منطق العمل، قواعد البيانات، كله في كود واحد متداخل ومترابط بشكل مرعب.

في ليلة من الليالي، كان لازم نضيف ميزة بسيطة جداً، شغلة ما بتاخذ يومين شغل في أي نظام محترم. لكن مع “الختيار”، القصة مختلفة. بعد أسابيع من العمل والحذر الشديد، جاء وقت الإطلاق. الساعة كانت 2 بعد منتصف الليل، وفريق كامل سهران على أمل كل شي يمشي تمام. ضغطنا زر النشر… وبعدها، انهار كل شيء. مش بس الميزة الجديدة ما اشتغلت، لأ، النظام كله صار يعطي أخطاء غريبة في أماكن ما إلنا علاقة فيها! قسم الفواتير توقف، وتقارير العملاء صارت تطلع فاضية. كانت كارثة.

تلك الليلة، وأنا بحاول أصلّح المشكلة مع الفريق، شعرت بإحباط ما بعده إحباط. حسينا حالنا محبوسين مع وحش ما بنقدر نلمسه، وأي محاولة لتطويره أو تحسينه ممكن تقتله وتقتل شغلنا معه. وقتها عرفت إنه لازم نلاقي طريقة ثانية، طريقة “جراحية” مش “تفجيرية”. وهنا بدأت رحلتنا مع نمط برعمت فكرته من الطبيعة نفسها: نمط التين الخانق.

ما هو هذا “الوحش” الذي نسميه المونوليث؟

قبل ما نغوص في الحل، خلينا نفهم المشكلة كويس. التطبيق المونوليثي (Monolithic Application) هو تطبيق مبني كوحدة واحدة متكاملة. تخيلوا بناية كاملة عبارة عن كتلة خرسانية واحدة، ما فيها غرف منفصلة. في البداية، هذا الأسلوب بيكون ممتاز: سهل التطوير، سهل الاختبار، وسهل النشر. كل شيء في مكان واحد.

لكن مع مرور الوقت ونمو التطبيق، بتبدأ الكوابيس:

  • الترابط الشديد (Tight Coupling): أي تغيير صغير في جزء ممكن يسبب مشاكل غير متوقعة في جزء آخر تماماً (زي ما صار معنا).
  • صعوبة التطوير: إضافة ميزة جديدة بتصير عملية بطيئة ومعقدة ومحفوفة بالمخاطر.
  • التقنية القديمة: التطبيق كله محبوس في إطار عمل أو لغة برمجة قديمة، وتحديثها يعني إعادة كتابة كل شيء.
  • صعوبة التوسع (Scaling): إذا احتجت تزيد قدرة جزء معين من التطبيق (مثلاً، خدمة البحث)، لازم تنسخ التطبيق كله وتزيد قدرته بالكامل، وهذا هدر كبير للموارد.

بنوصل لمرحلة بنخاف نلمس الكود، وبنصير نردد المقولة الشهيرة: “إذا شغال، لا تلمسه!”. لكن السوق ما برحم، والمنافسين ما بستنوا، والتطوير لازم يستمر.

بصيص الأمل: تعرف على “نمط التين الخانق” (Strangler Fig Pattern)

وسط هذا اليأس، وجدنا ضالتنا في مقال للمهندس العظيم “مارتن فاولر” يتحدث فيه عن نمط اسمه “نمط التين الخانق”. الاسم مستوحى من شجرة التين الاستوائية اللي بتنمو بإنها تلتف حول شجرة ثانية أكبر وأقدم منها. مع الوقت، التينة بتكبر وبتقوى، جذورها بتنزل للأرض، وأغصانها بتحجب الشمس عن الشجرة القديمة. في النهاية، الشجرة القديمة بتموت وبتتحلل، وما بضل مكانها إلا شجرة التين الجديدة القوية.

الفكرة بسيطة وعبقرية: بدل ما نهدم “الختيار” (المونوليث) ونبني واحد جديد من الصفر (وهو ما يسمى بالـ Big Bang Rewrite، واللي غالباً بفشل)، رح نبني النظام الجديد “حول” النظام القديم بشكل تدريجي. بنبدأ نخنق النظام القديم شوي شوي، وبنسحب منه الوظائف وحدة ورا الثانية، لحد ما يفضى تماماً ويصير جاهز “للموت” بسلام.

خارطة الطريق: كيف طبقنا النمط خطوة بخطوة؟

هذا النمط مش مجرد فكرة نظرية، هو استراتيجية عملية جداً. خلوني أشرح لكم الخطوات اللي مشيناها بالتفصيل.

الخطوة الأولى: تحديد “واجهة الخنق” (The Strangler Façade)

أول وأهم خطوة هي بناء “واجهة” أو “بوابة” (Proxy/Gateway) بسيطة جداً. هذه البوابة رح تكون النقطة اللي بتستقبل كل الطلبات من المستخدمين قبل ما توصل للتطبيق. في البداية، كل وظيفة هاي البوابة هي إنها تمرر كل الطلبات زي ما هي للتطبيق القديم (المونوليث).

هذه البوابة هي اللي رح تعطينا القدرة على “توجيه” الطلبات لاحقاً. ممكن تكون عبارة عن Reverse Proxy بسيط باستخدام أدوات مثل Nginx أو HAProxy، أو ممكن تكون تطبيق صغير مبني بـ Node.js أو أي لغة سريعة.

نصيحة أبو عمر: ابدأ بأبسط بوابة ممكنة. لا تعقد الأمور. هدفك الأول هو وضع نفسك في مسار الطلبات للتحكم فيها. لاحقاً يمكنك تطوير هذه البوابة.

كمثال، لو استخدمنا Nginx، الإعدادات الأولية ممكن تكون بسيطة جداً:


# nginx.conf

server {
    listen 80;
    server_name yourapp.com;

    location / {
        # في البداية، كل الطلبات تذهب إلى التطبيق القديم "الختيار"
        proxy_pass http://legacy-monolith-app:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

بهذه الخطوة، صار عنا نقطة تحكم. كل الزوار بدخلوا من باب واحد إحنا حراسه.

الخطوة الثانية: اختيار أول “غصن” للزراعة

الآن تبدأ المتعة. لازم نختار أول جزء من “الختيار” بدنا نستبدله. اختيار الجزء الأول مهم جداً لأنه رح يعطينا دفعة معنوية وثقة. كيف نختار؟

  • اختر جزءاً قليل المخاطر: لا تبدأ بأكثر جزء حساس في النظام (مثل نظام الدفع). ابدأ بشيء مثل صفحة “من نحن” أو نظام إدارة ملفات المستخدمين الشخصية.
  • اختر جزءاً ذا قيمة تجارية واضحة: إذا كان قسم التسويق يطلب تغييرات مستمرة على صفحة العروض مثلاً، فهذا مرشح ممتاز. استبدالها سيعطيهم المرونة التي يريدونها بسرعة.
  • اختر جزءاً معزولاً نسبياً: كلما كان الجزء أقل اعتمادية على أجزاء أخرى، كانت عملية فصله أسهل.

في حالتنا، اخترنا نظام إدارة “ملفات المستخدمين” (User Profiles). كان نظاماً قديماً، وبحاجة ماسة لواجهة جديدة وميزات إضافية.

الخطوة الثالثة: البناء والتوجيه (Build and Redirect)

الآن نقوم ببناء هذا الجزء (ملفات المستخدمين) كتطبيق مصغّر (Microservice) جديد ومستقل، باستخدام أحدث التقنيات اللي بنحبها (مثلاً، Node.js مع قاعدة بيانات MongoDB). هذا التطبيق الجديد له قاعدة بياناته الخاصة ومنطق العمل الخاص به.

بعد ما يصير جاهز، بنرجع لـ “واجهة الخنق” (بوابة Nginx) وبنعدّل الإعدادات. الآن، أي طلب يخص ملفات المستخدمين (مثلاً، أي طلب يبدأ بـ `/api/users/` أو `/profile`) سيتم توجيهه للخدمة الجديدة، بينما باقي الطلبات ستستمر بالذهاب إلى “الختيار”.


# nginx.conf - بعد التحديث

# عنوان الخدمة الجديدة
upstream new_profile_service {
    server new-profile-app:3000;
}

server {
    listen 80;
    server_name yourapp.com;

    # توجيه الطلبات الخاصة بالملفات الشخصية إلى الخدمة الجديدة
    location /api/users/ {
        proxy_pass http://new_profile_service;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
    
    location /profile {
        proxy_pass http://new_profile_service;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # باقي الطلبات تذهب إلى "الختيار" كالعادة
    location / {
        proxy_pass http://legacy-monolith-app:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

وهيك بنكون زرعنا أول غصن ناجح! المستخدمين ما بحسوا بأي فرق، لكن خلف الكواليس، قطعة من الوحش القديم تم استبدالها بقطعة جديدة، مرنة، وحديثة.

الخطوة الرابعة: التعامل مع البيانات (وهنا التحدي الأكبر)

هذه أصعب خطوة عادةً. كيف تتعامل الخدمة الجديدة مع البيانات التي قد تكون مشتركة مع النظام القديم؟ هناك عدة استراتيجيات، وما في حل واحد يناسب الجميع:

  1. قاعدة بيانات مشتركة (مؤقتاً): في البداية، ممكن للخدمة الجديدة أن تقرأ وتكتب في نفس قاعدة بيانات المونوليث القديم. هذا حل سريع لكنه خطير لأنه يحافظ على الترابط. استخدمه بحذر شديد وكمرحلة انتقالية فقط.
  2. مزامنة البيانات (Data Synchronization): ممكن بناء آلية لمزامنة البيانات بين قاعدة بيانات المونوليث القديمة وقاعدة البيانات الجديدة الخاصة بالخدمة الجديدة. يمكن استخدام أدوات مثل Debezium (لـ Change Data Capture) أو أنظمة الرسائل (Message Queues) مثل RabbitMQ أو Kafka. عندما يتم تحديث بيانات المستخدم في النظام القديم، يتم إرسال “حدث” (Event) للخدمة الجديدة لتحديث بياناتها، والعكس صحيح.
  3. طبقة واجهة برمجة التطبيقات (API Layer): إذا كانت الخدمة الجديدة تحتاج بيانات لا تملكها (مثلاً، تحتاج قائمة طلبات المستخدم، وهي ما زالت في المونوليث)، يمكنها أن تستدعي API في المونوليث للحصول على هذه البيانات.

نصيحة أبو عمر: ابدأ بالحل الأبسط الذي يفي بالغرض. هدفك هو فصل الوظيفة أولاً، ثم يمكنك تحسين استراتيجية البيانات لاحقاً. لا تسعَ للكمال من اليوم الأول، بل للتطور التدريجي.

الخطوة الخامسة: التكرار حتى “الاختناق” الكامل

الآن، ما عليك إلا تكرار العملية:

  1. اختر الجزء التالي من المونوليث (مثلاً، نظام الفواتير).
  2. ابنه كخدمة مصغّرة جديدة.
  3. حدّث بوابة التوجيه (Nginx) لتوجيه الطلبات الخاصة بالفواتير للخدمة الجديدة.
  4. عالج تحديات البيانات.
  5. أطلق الخدمة الجديدة.

مع كل تكرار، المونوليث القديم “الختيار” يصغر ويضعف، وتقل مسؤولياته. والنظام الجديد ينمو ويكبر مثل شجرة التين القوية. وفي يوم من الأيام، ستجد أن المونوليث لم يعد يفعل أي شيء مهم. كل وظائفه تم نقلها. في تلك اللحظة السعيدة، يمكنك إيقافه وحذفه إلى الأبد. торжество!

الخلاصة: من وحش كاسر إلى حديقة خدمات يانعة 🌳

رحلتنا مع “الختيار” كانت طويلة وصعبة، لكن نمط التين الخانق كان هو المنقذ. بدل المخاطرة بكل شيء في عملية إعادة كتابة فاشلة، اتبعنا استراتيجية ذكية، صبورة، وقليلة المخاطر.

أهم الدروس اللي تعلمناها:

  • النمو التدريجي أفضل من الانفجار الكبير: لا تحاول حل كل شيء مرة واحدة. القيمة الحقيقية تأتي من التحسينات الصغيرة والمستمرة.
  • التحكم هو مفتاح النجاح: “واجهة الخنق” هي أهم قطعة في اللغز، فهي تعطيك القدرة على التوجيه والتجربة والتراجع بأمان.
  • احتفل بالانتصارات الصغيرة: كل خدمة جديدة تطلقها هي انتصار كبير لفريقك. احتفل به، فهذا يبني الزخم والثقة.

إذا كنت تواجه وحشاً مونوليثياً في عملك وتشعر أنك محاصر، لا تيأس. تذكر قصة شجرة التين. لا تخف من “الختيار”، بل تعلم كيف تزرع حوله المستقبل، غصناً بعد غصن. صدقني، الشعور الذي ستحصل عليه عندما تضغط زر الحذف النهائي على النظام القديم… لا يقدر بثمن.

بالتوفيق يا جماعة الخير!

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

أتمتة العمليات

إشعاراتنا كانت ضجيجاً والمهام تتطلب التنقل بين ألف شاشة: كيف أنقذنا ChatOps من جحيم الفوضى التشغيلية؟

أشارككم تجربتي كـ"أبو عمر"، مبرمج فلسطيني، في تحويل فوضى الإشعارات والتنقل بين الأنظمة إلى بيئة عمل منظمة وفعالة باستخدام ChatOps. اكتشفوا كيف أتمتنا عملياتنا، ووفرنا...

12 أبريل، 2026 قراءة المزيد
نصائح برمجية

شروطنا المتشعبة كانت متاهة: كيف أنقذتنا ‘شروط الحماية’ (Guard Clauses) من جحيم الـ if-else المتداخل؟

هل تعاني من تداخل الشروط في الكود؟ أشاركك قصة حقيقية وكيف غيّرت 'شروط الحماية' (Guard Clauses) طريقة كتابتي للكود، محولةً المتاهات المعقدة إلى مسارات واضحة...

12 أبريل، 2026 قراءة المزيد
​معمارية البرمجيات

خدماتنا كانت متشابكة كخيوط العنكبوت: كيف أنقذتنا ‘المعمارية الموجهة بالأحداث’ (EDA) من جحيم الاعتمادية المباشرة؟

أشارككم قصة حقيقية من تجربتي كـ "أبو عمر" المبرمج، وكيف كانت خدماتنا على وشك الانهيار بسبب التشابك والاعتمادية. اكتشفوا معنا كيف كانت "المعمارية الموجهة بالأحداث"...

12 أبريل، 2026 قراءة المزيد
ذكاء اصطناعي

قرارات نموذجنا كانت صندوقاً أسود: كيف أنقذتنا تقنيات التفسير (XAI) من جحيم التنبؤات الغامضة؟

أشارككم قصة من الميدان، يوم كاد نموذج ذكاء اصطناعي "صندوق أسود" أن يورطنا في قرارات كارثية. هذه المقالة هي دليلك لفهم تقنيات الذكاء الاصطناعي القابل...

12 أبريل، 2026 قراءة المزيد
خوارزميات

قاعدة بياناتنا كانت تستغيث: كيف أنقذنا ‘فلتر بلوم’ من جحيم التحقق المكلف من وجود العناصر؟

أشارككم قصة حقيقية من ميدان المعركة البرمجية، يوم كادت قاعدة بياناتنا أن تنهار تحت ضغط عمليات التحقق المتكررة. سأكشف لكم عن السلاح السري الذي أنقذنا،...

12 أبريل، 2026 قراءة المزيد
تجربة المستخدم والابداع البصري

تطبيقنا كان حصناً منيعاً: كيف أنقذتنا ‘معايير الوصول الرقمي (WCAG)’ من جحيم الإقصاء الرقمي؟

أشارككم قصة حقيقية حول كيف أدركنا أن تطبيقنا "المثالي" كان يقصي شريحة كبيرة من المستخدمين. هذه المقالة هي رحلتنا في فهم وتطبيق معايير الوصول الرقمي...

11 أبريل، 2026 قراءة المزيد
البودكاست