تطبيقي المتجانس كان وحشاً لا يمكن ترويضه: كيف أنقذني ‘نمط الخانق’ (Strangler Fig Pattern) من جحيم إعادة الكتابة الكبرى؟

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

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

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

ما هو التطبيق المتجانس (Monolith)؟ ولماذا يصبح وحشاً؟

قبل أن نغوص في الحل، دعونا نفهم المشكلة. التطبيق المتجانس، أو “المونوليث”، هو تطبيق برمجي مبني كوحدة واحدة متكاملة. كل شيء – واجهة المستخدم، منطق العمل (Business Logic)، الوصول للبيانات – موجود في قاعدة كود واحدة ويتم نشره ككتلة واحدة.

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

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

فكرة “إعادة الكتابة الكبرى”: حلم وردي أم كابوس محقق؟

عندما تصل الأمور إلى هذا الحد من السوء، يظهر اقتراح “إعادة الكتابة الكبرى” (The Big Bang Rewrite) كحل سحري. “لنهدم البيت القديم ونبني قصراً جديداً مكانه!”. الفكرة مغرية جداً، لكنها من أخطر الفخاخ في عالم هندسة البرمجيات.

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

المشكلة أن متطلبات العمل لا تتوقف. بينما يقضي فريقك سنتين في إعادة بناء النظام، سيكون السوق قد تغير، والعملاء طلبوا ميزات جديدة، والمنتج الجديد الذي ستطلقه سيكون قديماً بالفعل يوم إطلاقه.

الحل السحري: نمط شجرة التين الخانقة (Strangler Fig Pattern)

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

في عالم البرمجيات، الفكرة مماثلة تماماً:

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

هذا النهج يسمح لنا بتحديث نظامنا بشكل تدريجي، آمن، ودون إيقاف عجلة العمل.

كيف طبقنا ‘نمط الخانق’ خطوة بخطوة؟

هذه هي الخطة العملية التي اتبعناها لترويض وحشنا “الرزق”:

الخطوة الأولى: بناء الواجهة الخانقة (The Strangler Façade)

أول وأهم خطوة هي إنشاء واجهة أمامية (Façade) تكون هي نقطة الدخول الوحيدة لكل الطلبات الموجهة لنظامنا. هذه الواجهة هي التي ستقرر ما إذا كان الطلب سيذهب إلى التطبيق المتجانس القديم أم إلى الخدمة المصغرة الجديدة. في حالتنا، استخدمنا بروكسي عكسي (Reverse Proxy) مثل Nginx.

في البداية، كل ما يفعله هذا البروكسي هو تمرير كل الطلبات كما هي إلى التطبيق القديم.


# nginx.conf (المرحلة الأولى)

server {
    listen 80;
    server_name alrizq.com;

    # في البداية، كل الطلبات تذهب إلى المونوليث القديم
    location / {
        proxy_pass http://legacy_monolith_app:8080;
        proxy_set_header Host $host;
    }
}

الخطوة الثانية: تحديد أول ضحية (أول خدمة للانتقال)

هنا الحكمة مطلوبة. لا تبدأ بأكثر أجزاء النظام تعقيداً (مثل نظام الدفع). ابدأ بشيء بسيط ومعزول نسبياً. في حالتنا، اخترنا وحدة “ملفات المستخدمين الشخصية” (User Profiles).

نصيحة من أبو عمر: اختر فوزاً سهلاً وسريعاً في البداية (a quick win). هذا يرفع من معنويات الفريق، ويثبت للإدارة أن الخطة ناجحة، ويعطيك فرصة لتعلم وتصحيح الأخطاء في جزء قليل المخاطر من النظام.

الخطوة الثالثة: تطوير ونشر الخدمة الجديدة

قمنا ببناء خدمة مصغرة جديدة لإدارة ملفات المستخدمين. استخدمنا فيها تقنيات أحدث (Python مع FastAPI) بدلاً من الـ Java القديمة في المونوليث. هذه إحدى أروع ميزات هذا النمط: حرية اختيار التكنولوجيا الأنسب لكل خدمة. قمنا بنشر هذه الخدمة بشكل مستقل تماماً.

الخطوة الرابعة: التوجيه التدريجي للطلبات

الآن نعود إلى الواجهة الخانقة (Nginx) ونقوم بتعديلها. سنطلب منها أن توجه أي طلب يذهب إلى /api/users إلى الخدمة الجديدة، بينما باقي الطلبات تظل تذهب إلى النظام القديم.


# nginx.conf (المرحلة الثانية)

server {
    listen 80;
    server_name alrizq.com;

    # توجيه طلبات المستخدمين إلى الخدمة المصغرة الجديدة
    location /api/users {
        proxy_pass http://new_users_microservice:8000;
        proxy_set_header Host $host;
    }

    # باقي الطلبات لا تزال تذهب إلى المونوليث القديم
    location / {
        proxy_pass http://legacy_monolith_app:8080;
        proxy_set_header Host $host;
    }
}

بهذه البساطة، بدأنا عملية “الخنق”! المستخدمون لن يلاحظوا أي فرق، لكننا في الخلفية بدأنا باستبدال أجزاء المحرك القديم وهو لا يزال يعمل.

الخطوة الخامسة: اغسل وكرر (Rinse and Repeat)

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

الخطوة السادسة: إعلان وفاة القديم

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

تحديات ونصائح من قلب المعركة

الرحلة لم تكن سهلة بالكامل، وواجهتنا بعض التحديات التي أود مشاركتها معكم:

  • مزامنة البيانات: كيف تتعامل الخدمة الجديدة مع البيانات التي لا تزال في قاعدة بيانات المونوليث؟ في البداية، قد تضطر للسماح للخدمة الجديدة بالوصول إلى قاعدة البيانات القديمة (مع الحذر الشديد). الحل الأفضل على المدى الطويل هو استخدام أنماط مثل “مزامنة البيانات عبر الأحداث” (Event-driven data synchronization) حيث يقوم المونوليث بإصدار حدث (event) عند أي تغيير في البيانات، وتستمع له الخدمات الجديدة لتحديث قواعد بياناتها الخاصة.
  • المعاملات الموزعة (Distributed Transactions): ماذا لو كانت عملية واحدة تتطلب تحديث البيانات في خدمة جديدة والمونوليث في نفس الوقت؟ هذه مشكلة معقدة. يجب تجنبها قدر الإمكان عن طريق تقسيم الوظائف بشكل جيد. إذا كانت ضرورية، فابحث عن أنماط متقدمة مثل نمط “Saga”.
  • التوثيق: “وثّق كل خطوة. خلي خريطة الطريق واضحة للكل”. من السهل أن تضيع في التفاصيل. حافظ على وثيقة مركزية توضح الأجزاء التي تم نقلها، والأجزاء قيد النقل، وما تبقى في المونوليث.

الخلاصة: لا تهدم البيت القديم قبل بناء الجديد 🏡

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

هذا النمط أنقذنا من كارثة محققة، وسمح لنا بتحديث نظامنا بأقل قدر من المخاطر، مع الاستمرار في تقديم قيمة للعملاء طوال فترة الانتقال. إنه ليس حلاً سريعاً، بل هو ماراثون يتطلب الصبر والتخطيط، لكن نتائجه مضمونة وآمنة.

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

أبو عمر

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

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

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

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

آخر المدونات

خوارزميات

مساراتي كانت متاهة: كيف أنقذتني خوارزمية ‘ديكسترا’ من جحيم التخطيط العشوائي؟

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

2 أبريل، 2026 قراءة المزيد
برمجة وقواعد بيانات

بياناتي كانت تتلف في صمت: كيف أنقذتني ‘المعاملات’ (Transactions) من جحيم العمليات غير المكتملة؟

أشارككم قصة من قلب المعركة البرمجية، حين كادت بيانات متجر إلكتروني أن تنهار بسبب عمليات غير مكتملة. اكتشفوا معي كيف أن مفهوم "المعاملات" (Transactions) ومبادئ...

2 أبريل، 2026 قراءة المزيد
الشبكات والـ APIs

واجهاتي البرمجية كانت فوضى: كيف أنقذتني ‘بوابة الـ API’ من جحيم إدارة الخدمات المتعددة؟

أشارككم تجربتي الشخصية كـ "أبو عمر" مع الفوضى التي سببتها الخدمات المصغرة (Microservices) في أحد مشاريعي، وكيف كانت "بوابة الواجهات البرمجية" (API Gateway) هي طوق...

2 أبريل، 2026 قراءة المزيد
الحوسبة السحابية

بنيتي التحتية كانت قصراً من ورق: كيف أنقذني ‘الكود كبنية تحتية’ (IaC) من جحيم الإعداد اليدوي والكوارث؟

أنا أبو عمر، مبرمج فلسطيني، وأروي لكم قصتي مع ليلة كادت أن تنهار فيها كل أنظمتي. اكتشفوا معي كيف حوّلت الفوضى إلى نظام، والكوارث إلى...

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