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

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

خلوني أحكيلكم قصة صارت معي قبل كم سنة، قصة بتجسّد كابوس كل مهندس برمجيات. كنت وقتها مسؤول عن فريق في شركة ناشئة، وكان عنا منتج طموح مبني على معمارية الخدمات المصغرة (Microservices). في ليلة من ليالي الشتاء، وأنا قاعد بحاول أستمتع بكاسة شاي بالمرمية، رن جوالي. على الطرف الثاني كان صوت مهندس صغير بالفريق، صوته كله توتر: “أبو عمر، الحق! النظام كله واقع!”.

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

بس شو دخل خدمة الإشعارات بالتسجيل أو بالطلبات؟ هون كانت الكارثة. اكتشفنا إن خدماتنا كانت متشابكة ببعضها بطريقة بشعة. خدمة التسجيل بعد ما تسجل المستخدم، كانت بتنادي بشكل مباشر (Synchronous API Call) خدمة الإشعارات عشان تبعتله إيميل ترحيب. ولأنه خدمة الإشعارات كانت واقعة، الطلب ما برجع، فبتضل خدمة التسجيل تستنى… وتستنى… لحد ما ينتهي الوقت (Timeout) ويفشل طلب التسجيل كله! وهكذا مع باقي الخدمات. كانت خيوط عنكبوت متشابكة، أي اهتزاز في خيط واحد كان بيهدم الشبكة كلها. في هذيك الليلة، واحنا بنحاول نصلح المشكلة، قلت لفريقي: “خلص، بكفي هيك. لازم نلاقي حل جذري لهالتشابك. لازم نحرر خدماتنا من بعضها”. وهون بلشت رحلتنا مع المعمارية القائمة على الأحداث (Event-Driven Architecture).

ما هو “التشابك الخانق” (Tight Coupling)؟

قبل ما نحكي عن الحل، خلينا نفهم أصل المشكلة اللي عانينا منها. المشكلة هاي إلها اسم تقني: Tight Coupling أو “الاقتران المحكم” أو زي ما بحب أسميه “التشابك الخانق”.

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

لما خدمة (أ) بتنادي خدمة (ب) بشكل مباشر عشان تأدي وظيفة معينة، بصير بينهم اقتران. خدمة (أ) الآن تعتمد بشكل كامل على وجود وصحة خدمة (ب).

مساوئ هذا التشابك

  • فشل متسلسل (Cascading Failures): زي ما صار معنا بالضبط. فشل خدمة واحدة غير مهمة يمكن أن يؤدي إلى انهيار النظام بأكمله.
  • بطء التطوير: أي تغيير في خدمة (ب) (مثلاً تغيير في الـ API) يتطلب تغيير وتحديث واختبار وإعادة نشر كل الخدمات اللي بتعتمد عليها، زي خدمة (أ). هذا بخلي عملية التطوير بطيئة ومعقدة.
  • صعوبة التوسع (Scalability): لو خدمة (أ) عليها ضغط كبير، وأنت بدك تعملها Scale Out (تزيد عدد الـ instances منها)، أنت مضطر كمان تعمل Scale لخدمة (ب) اللي هي بتناديها، حتى لو خدمة (ب) نفسها ما عليها ضغط. أنت بتكبر كل شيء مع بعضه.

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

المنقذ: المعمارية القائمة على الأحداث (EDA)

هنا يأتي دور البطل في قصتنا: Event-Driven Architecture (EDA). الفكرة وراها عبقرية في بساطتها: بدلاً من أن تتحدث الخدمات مع بعضها البعض مباشرة، هي تتواصل بشكل غير مباشر من خلال “الأحداث” (Events).

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

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

المكونات الأساسية لـ EDA

أي نظام EDA يتكون من ثلاثة أجزاء رئيسية:

  1. منتج الحدث (Event Producer): هو الخدمة التي تولّد الحدث وتنشره. في قصتنا، خدمة التسجيل هي منتج لحدث UserRegistered.
  2. مستهلك الحدث (Event Consumer): هو الخدمة (أو الخدمات) التي تستمع إلى الأحداث وتتفاعل معها. في قصتنا، خدمة الإشعارات وخدمة التحليلات كلاهما مستهلكان لحدث UserRegistered.
  3. وسيط الأحداث (Event Broker / Router): هذا هو القلب النابض للنظام. هو القناة أو المنصة المركزية التي تستقبل الأحداث من المنتجين وتوزعها على المستهلكين المهتمين. هو بمثابة “محطة الإذاعة” في مثالنا. أشهر التقنيات المستخدمة هنا هي Apache Kafka, RabbitMQ, AWS SQS, أو Google Cloud Pub/Sub.

لنطبق هذا على قصتنا: كيف تغيرت الأمور؟

بعد ليلتنا المشؤومة، أعدنا تصميم الجزء الخاص بالتسجيل باستخدام EDA. انظروا كيف تغير السيناريو بالكامل:

قبل EDA (النموذج المتشابك)

  1. المستخدم يضغط “تسجيل”.
  2. RegistrationService يستقبل الطلب.
  3. RegistrationService ينشئ المستخدم في قاعدة البيانات.
  4. RegistrationService ينادي بشكل مباشر NotificationService لإرسال إيميل ترحيبي (وهنا كانت نقطة الفشل).
  5. NotificationService واقعة، الطلب يفشل بعد 30 ثانية.
  6. RegistrationService يفشل، ويرجع خطأ للمستخدم. المستخدم غاضب، ونحن في ورطة.

بعد EDA (النموذج المرن)

  1. المستخدم يضغط “تسجيل”.
  2. RegistrationService يستقبل الطلب.
  3. RegistrationService ينشئ المستخدم في قاعدة البيانات.
  4. RegistrationService ينشر حدثاً اسمه UserRegistered إلى وسيط الأحداث (مثلاً Kafka). هذا الحدث يحتوي على معلومات مثل user_id و email.
  5. RegistrationService يرجع فوراً رسالة نجاح للمستخدم: “تم تسجيلك بنجاح!”.

لاحظت الفرق؟ مهمة خدمة التسجيل انتهت عند الخطوة الخامسة. المستخدم حصل على استجابة سريعة وإيجابية. لكن ماذا عن الإيميل الترحيبي؟

هنا يكمل السحر عمله في الخلفية وبشكل غير متزامن (Asynchronously):

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

مثال كود بسيط جداً (Pseudocode)

لتقريب الصورة، هذا شكل الكود كيف ممكن يكون:

المنتج (Producer – خدمة التسجيل)


# UserService.py
def register_user(email, password):
  # 1. إنشاء المستخدم في قاعدة البيانات
  user = db.create_user(email, password)

  # 2. تجهيز الحدث
  event_payload = {
    "event_type": "UserRegistered",
    "data": {
      "user_id": user.id,
      "email": user.email,
      "registered_at": datetime.now().isoformat()
    }
  }

  # 3. نشر الحدث إلى وسيط الأحداث
  event_broker.publish(topic="user_events", event=event_payload)

  # 4. إرجاع استجابة فورية وناجحة
  return {"status": "success", "message": "User registration initiated."}

المستهلك (Consumer – خدمة الإشعارات)


# NotificationService.py
def process_user_events():
  # حلقة لا نهائية للاستماع للأحداث
  while True:
    event = event_broker.consume(topic="user_events")

    if event and event["event_type"] == "UserRegistered":
      user_email = event["data"]["email"]
      try:
        # محاولة إرسال الإيميل
        email_provider.send_welcome_email(to=user_email)
        # تم بنجاح، احذف الحدث من الطابور
        event.acknowledge() 
      except Exception as e:
        # فشل الإرسال، لا تحذف الحدث لكي تتم إعادة المحاولة لاحقاً
        # أو أرسله إلى طابور الرسائل الميتة (Dead-Letter Queue)
        print(f"Failed to send email: {e}")
        event.requeue()

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

نصائح من مطبخ أبو عمر (خبرة عملية)

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

  • ابدأ صغيراً: لا تحاول تحويل نظامك كله مرة واحدة. اختر عملية واحدة غير حرجة (non-critical) في نظامك، مثل تحديث ملف شخصي، وحوّلها لاستخدام الأحداث. تعلم منها، ثم توسع.
  • اختر وسيط الأحداث بعناية: “مش كل إشي بنفع لكل إشي”. Kafka ممتاز للتعامل مع كميات ضخمة من البيانات وتدفقها (Streaming) ويضمن ترتيب الأحداث. RabbitMQ مرن جداً ويوفر أنماط توجيه معقدة. AWS SQS بسيط للغاية ومناسب لطوابير المهام البسيطة. ادرس الفروقات واختر ما يناسب احتياجك.
  • صمم أحداثك كعقد (Events as Contracts): الحدث هو API عام. صممه بعناية. اجعله غنياً بالمعلومات الكافية (لكن ليس كل شيء). استخدم أسماء واضحة. الأهم: فكر في إصدارات الأحداث (Event Versioning) من اليوم الأول، لأنه بعد فترة ستحتاج إلى تعديل شكل الحدث بدون كسر المستهلكين القدامى.
  • استعد للفوضى المنظمة: تتبع الأخطاء في نظام EDA أصعب من الأنظمة المتزامنة. لا يمكنك تتبع طلب واحد بسهولة. استثمر بقوة في أدوات المراقبة (Monitoring) والتتبع الموزع (Distributed Tracing) والـ Logging المركزي. بدك تشوف القصة كاملة من أولها لآخرها، من لحظة نشر الحدث حتى استهلاكه في كل خدمة.
  • فكر في الـ Idempotency: ماذا لو استهلكت خدمة الإشعارات نفس الحدث مرتين بالخطأ؟ هل سترسل إيميلين للمستخدم؟ يجب أن يكون المستهلكون مصممين بحيث أن تنفيذ نفس الحدث عدة مرات يعطي نفس النتيجة كأنه نُفذ مرة واحدة. هذا مبدأ مهم جداً اسمه Idempotency.

الخلاصة: هل EDA للجميع؟

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

  • Decoupling (فك الارتباط): أصبحت الخدمات مستقلة تماماً.
  • Resilience (المرونة): فشل خدمة لم يعد يعني فشل النظام.
  • Scalability (قابلية التوسع): يمكننا الآن توسيع كل خدمة على حدة بناءً على حمل العمل الخاص بها.
  • Flexibility (المرونة): إضافة خدمة جديدة تستمع إلى الأحداث الحالية أصبح سهلاً جداً بدون تعديل أي كود قديم.

لكن، هل هي الحل لكل المشاكل؟ طبعاً لا. في الأنظمة البسيطة جداً أو التطبيقات الموحدة (Monoliths)، قد تكون EDA تعقيداً لا داعي له. لكن في عالم الخدمات المصغرة (Microservices)، الأنظمة الموزعة، تطبيقات إنترنت الأشياء (IoT)، أو أي نظام يحتاج إلى معالجة بيانات في الوقت الفعلي، تصبح EDA ليست مجرد خيار، بل ضرورة.

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

أبو عمر

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

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

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

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

آخر المدونات

برمجة وقواعد بيانات

كانت تحديثات قاعدة البيانات كابوساً: كيف أنقذتنا أدوات الترحيل (Migrations) من جحيم التعديلات اليدوية؟

هل عانيت يوماً من تحديث مخطط قاعدة البيانات يدوياً بين فريقك؟ أبو عمر يشارككم قصة حقيقية حول كيف غيّرت أدوات الترحيل (Migrations) طريقة عمل فريقه،...

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

كانت خوادمنا تستجدي التحديثات: كيف أنقذتنا ‘خطاطيف الويب’ (Webhooks) من جحيم الاستقصاء المستمر (Polling)؟

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

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

كانت بنيتنا التحتية قصراً من رمال: كيف أنقذتنا “البنية التحتية ككود” (IaC) من جحيم البيئات المتضاربة؟

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

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

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

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

17 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كانت قاعدة بياناتنا تتوسل الرحمة: كيف أنقذنا التخزين المؤقت (Caching) من جحيم الاستعلامات البطيئة

قصة حقيقية من واقع العمل عن كيفية انهيار نظامنا تحت ضغط الاستعلامات المتكررة، وكيف كان التخزين المؤقت (Caching) هو طوق النجاة. مقالة عملية للمطورين تشرح...

17 مايو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

كان التحقق من هوية عملائنا يستغرق أياماً: كيف أنقذنا الذكاء الاصطناعي (eKYC) من جحيم الإجراءات اليدوية؟

بصفتي مبرمجاً فلسطينياً، سأروي لكم حكايتنا مع كابوس التحقق اليدوي من هوية العملاء (KYC) وكيف كانت رحلة الانتقال إلى التحقق الإلكتروني (eKYC) باستخدام الذكاء الاصطناعي...

17 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

كانت أعطالنا تكتشف بعد فوات الأوان: كيف أنقذنا Prometheus من جحيم المراقبة التفاعلية؟

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

17 مايو، 2026 قراءة المزيد
ادارة الفرق والتنمية البشرية

كانت فرقنا صامتة أمام الأخطاء: كيف أنقذتنا ‘السلامة النفسية’ من جحيم ثقافة اللوم؟

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

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