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

يا جماعة الخير، السلام عليكم ورحمة الله.

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

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

وهون بدأت رحلتي مع عالم الـ Microservices أو “الخدمات المصغرة”.

ما هو الجحيم الذي كنت أعيش فيه؟ معمارية الكتلة الواحدة (Monolith)

قبل ما نحكي عن الحل، خلينا نفهم أصل المشكلة. تطبيقي “دكانتي” كان مبني على ما يسمى بالـ Monolithic Architecture. تخيلها كأنها عمارة سكنية، بس كل الشقق فيها مفتوحة على بعض، والمطبخ والحمام والصالون كلهم في غرفة واحدة كبيرة. كل شيء مترابط ببعضه بشكل مباشر.

في البداية، هذا الأسلوب ممتاز:

  • بساطة في البداية: كل شيء في مشروع واحد، سهل تبدأ وتبرمج.
  • سهولة النشر (مؤقتًا): هو ملف واحد أو حزمة واحدة بتنشرها وانتهى.
  • سهولة الاختبار: بما أنه كتلة واحدة، الاختبارات الشاملة (End-to-End) تكون أسهل نسبيًا.

لكن لما المشروع يكبر، بتبدأ الكوابيس…

عيوب الـ Monolith القاتلة

  • الصيانة المستحيلة: أي تعديل صغير يتطلب فهم أجزاء كبيرة من النظام خوفًا من كسر شيء آخر. الكود صار “سباغيتي كود” معقد ومترابط بشكل مرعب.
  • بطء التطوير والنشر: كما ذكرت في قصتي، عملية الـ build والـ deploy تستغرق وقتًا طويلاً جدًا، مما يقتل سرعة إطلاق الميزات الجديدة.
  • صعوبة التوسع (Scalability): لو كان عندك ضغط كبير على صفحة المنتجات فقط، بينما نظام الطلبات شبه فارغ، أنت مضطر تعمل scaling (توسيع) لكل التطبيق كوحدة واحدة، وهذا هدر كبير للموارد. لا يمكنك توسيع جزء دون الآخر.
  • التقيّد بتقنية واحدة: إذا بنيت تطبيقك بلغة Java مثلاً، فكل جزء فيه يجب أن يكون Java. لا يمكنك استخدام Python لميزة ذكاء اصطناعي جديدة أو Node.js لجزء يحتاج لسرعة في التعامل مع الطلبات، إلا بصعوبة بالغة.
  • نقطة فشل واحدة (Single Point of Failure): إذا حدث خطأ أو استهلك جزء من التطبيق (مثل نظام التقارير) كل الذاكرة، فالتطبيق بأكمله ينهار. وهذا ما حدث معي تمامًا.

المنقذ: معمارية الخدمات المصغرة (Microservices)

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

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

  • مسؤولة عن مجال عمل واحد (Business Domain). مثلاً: خدمة للمستخدمين، خدمة للمنتجات، خدمة للطلبات.
  • تعمل بشكل مستقل تمامًا عن الخدمات الأخرى.
  • لها قاعدة بياناتها الخاصة (وهذه نقطة مهمة جدًا).
  • يمكن تطويرها ونشرها وتوسيعها بشكل مستقل.
  • تتواصل مع الخدمات الأخرى عبر شبكة، غالبًا باستخدام APIs (مثل REST) أو أنظمة الرسائل (Message Queues).

تخيل معي تطبيق “دكانتي” بعد تحويله:

  • خدمة المنتجات (Products Service): مسؤولة عن كل ما يتعلق بالمنتجات. مكتوبة بلغة Go لأنها سريعة.
  • خدمة المستخدمين (Users Service): مسؤولة عن التسجيل والدخول. مكتوبة بلغة Java لأن الفريق خبير فيها.
  • خدمة الطلبات (Orders Service): مسؤولة عن إنشاء وتتبع الطلبات. مكتوبة بلغة #C.
  • خدمة الدفع (Payments Service): مسؤولة عن معالجة الدفعات. مكتوبة بلغة Python للتكامل مع مكتبات تحليل مالي.

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

كيف انتقلت من الجحيم إلى النعيم؟ خطوات عملية

الانتقال لم يكن سهلاً أو سريعًا، وهذا درس مهم. لا تقم بإعادة كتابة كل شيء من الصفر! هذا فخ. استخدمنا استراتيجية تسمى “نمط التين الخانق” (Strangler Fig Pattern). تخيل شجرة تين تنمو حول شجرة قديمة، ومع الوقت تخنقها وتأخذ مكانها. هذا ما فعلناه بالضبط.

الخطوة 1: تحديد “خطوط التماس” وتفكيك أول خدمة

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

الخطوة 2: بناء الخدمة المصغرة الجديدة

قمنا ببناء خدمة جديدة تمامًا اسمها products-service باستخدام Node.js و Express. هذه الخدمة لها قاعدة بياناتها الخاصة (نسخنا البيانات المتعلقة بالمنتجات إليها) وتوفر واجهة برمجية (API) بسيطة لجلب المنتجات.


// مثال بسيط جدًا لخدمة المنتجات باستخدام Express.js
const express = require('express');
const app = express();
const port = 3001;

// بيانات وهمية للمنتجات (في الواقع ستكون من قاعدة بيانات)
const products = [
    { id: 1, name: 'كوفية فلسطينية', price: 15 },
    { id: 2, name: 'زيت زيتون بكر', price: 25 },
];

app.get('/api/products', (req, res) => {
    console.log('Request received for products');
    res.json(products);
});

app.listen(port, () => {
    console.log(`Products service listening on port ${port}`);
});

الخطوة 3: التوجيه التدريجي (Strangling)

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

الخطوة 4: التواصل بين الخدمات

عندما احتجنا أن تقوم “خدمة الطلبات” (التي لا تزال داخل المونوليث مؤقتًا) بالتحقق من سعر منتج ما، جعلناها تتواصل مع الخدمة الجديدة عبر استدعاء API.


// مثال داخل خدمة الطلبات لكيفية استدعاء خدمة المنتجات
async function getProductPrice(productId) {
    try {
        // استدعاء الخدمة الجديدة عبر الشبكة
        const response = await fetch(`http://products-service:3001/api/products/${productId}`);
        const product = await response.json();
        return product.price;
    } catch (error) {
        console.error('Error fetching product data:', error);
        return null; // Handle error appropriately
    }
}

مع الوقت، كررنا هذه العملية مع خدمة المستخدمين، ثم الطلبات، وهكذا. شيئًا فشيئًا، “ماتت” الشجرة القديمة (المونوليث) وحلت محلها حديقة من الخدمات الصغيرة المرنة والقوية.

نصائح أبو عمر (من الكيس)

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

⚠️ لا تبدأ بالخدمات المصغرة!

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

🛠️ استثمر في الـ DevOps بقوة

إدارة خدمة واحدة شيء، وإدارة 20 خدمة شيء آخر تمامًا. ستحتاج إلى بنية تحتية قوية للنشر المستمر (CI/CD)، ومراقبة مركزية (Centralized Monitoring & Logging) باستخدام أدوات مثل Prometheus/Grafana أو ELK Stack، ونظام لتشغيل الحاويات مثل Docker و Kubernetes. هذه ليست رفاهية، بل ضرورة قصوى.

🗃️ فكّر في البيانات أولًا وأخيرًا

أصعب جزء في الخدمات المصغرة هو إدارة البيانات الموزعة. كل خدمة لها قاعدة بياناتها. كيف تضمن تناسق البيانات بين الخدمات؟ (مثلاً، عند حذف منتج، كيف تتأكد من حذفه من سلات التسوق لدى المستخدمين؟). ستحتاج لتعلم أنماط مثل Saga Pattern و Event Sourcing للتعامل مع هذه التحديات.

الخلاصة: حرية تأتي مع مسؤولية

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

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

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

والله ولي التوفيق.

أبو عمر

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

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

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

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

آخر المدونات

تجربة المستخدم والابداع البصري

واجهاتي كانت فوضى بصرية: كيف أنقذني ‘نظام التصميم’ (Design System) من جحيم عدم الاتساق؟

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

29 مارس، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

ملفي الشخصي على GitHub كان صامتاً: كيف حولتني المساهمات في المشاريع المفتوحة المصدر من مبرمج مجهول إلى مرشح مطلوب؟

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

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