قالوا سبحانك لا علم لنا إلا ما علمتنا إنك أنت العليم الحكيم 

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

هل هذا المشروع كبير بما يكفي لاستخدام الخدمات المصغرة (Microservices)؟ 

لست أدري لكن أترك الإجابة لك… لكن نصيحة لا تستخدم معمارية أو أداة ما لمجرد أنك تريد ذكرها في السيرة الذاتية. 

ما هي الخدمة بالضبط؟

الخدمة (Service) هي كيان برمجي مستقل يعمل كتطبيق منفصل على عكس الحزمة البرمجية (Package)، الخدمة ليست مجرد مكتبة يتم تضمينها داخل مشروع أكبر، بل هي نظام متكامل يحتوي على منطق خاص به، وعادة ما يتضمن واجهة برمجية (API) للتواصل مع الأنظمة الأخرى أو (Protocol Buffers).

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

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

ما الذي يجعل الخدمة "صغيرة" (Microservice)؟

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

لا يوجد معيار ثابت لتحديد حجم الخدمة، ولكن هنالك قاعدة كتبها سام نيومان في كتابه (Building Microservices) وهي أن تكون الخدمة صغيرة بما يكفي لإعادة كتابتها بالكامل خلال دورة تطوير أو اثنتين (Sprint).

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

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

الحفاظ على هذا التوازن بين حجم الخدمة وفوائدها وتعقيداتها هو جوهر معمارية الخدمات الصغيرة.

هنالك سؤالَا يطرح الآن في بالك، ماذا أفعل مع الفئات المشتركة ما بين الخدمات الصغيرة كيف أشاركها؟؟

لنعد إلى نظام إدارة المستشفى، يتم تقسيم النظام إلى سياقات محددة (Bounded Contexts)، مثل إدارة المرضى، إدارة المواعيد، والإدارة المالية، كل سياق يحتوي على نماذج داخلية (Internal Models) تُستخدم داخلياً لتنفيذ العمليات، ونماذج مشتركة (Shared Models) تُستخدم للتواصل مع السياقات الأخرى.

على سبيل المثال:

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

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

مثال على النموذج الداخلي والخارجي :

النموذج الداخلي (Internal Model) يشمل:

1. تفاصيل الموعد الكاملة (تاريخ، وقت)

2. حالة الموعد (مؤكد/ملغى)

3. اسم الطبيب المخصص للموعد

النموذج الخارجي (External Model) يشمل نسخة مختصرة تتضمن:

1. رقم الموعد (Appointment ID)

2. التاريخ والوقت فقط

لمزيد من المعلومات عن مصطلح "سياقات محددة (Bounded Contexts)" أنصحك بقراءة كتاب (Domain-Driven Design: Tackling Complexity in the Heart of Software).

تخزين البيانات في الخدمات الصغيرة

عند العمل بالخدمات الصغيرة (Microservices)، واحدة من أكبر التحديات هي كيفية تخزين البيانات.

النقطة الأهم هنا هي لا يجب أن تعتمد جميع الخدمات على قاعدة بيانات واحدة، هذا النهج يعزز استقلالية الخدمات ويتيح لكل خدمة اختيار نوع قاعدة البيانات الأنسب لاحتياجاتها، سواء كانت SQL أو NoSQL، بناءً على طبيعة البيانات ومتطلبات الأداء.

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

استخدام قاعدة بيانات واحدة لجميع الخدمات يؤدي إلى مشكلات قد طرحناها من قبل لكن لتتذكرها دائما:

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

لذلك، الحل الأفضل هو أن تكون لكل خدمة قاعدة بيانات خاصة بها.

التواصل بين الخدمات في نظام إدارة المستشفى

من أكبر التحديات في الخدمات الصغيرة هي كيفية تحقيق التواصل بين الخدمات المستقلة، يمكننا استخدام طريقتين رئيسيتين للتواصل:

1. نمط الطلب/الاستجابة (Request/Response Pattern)

2. التواصل غير المتزامن المعتمد على الأحداث (Event-Based, Asynchronous Communication)

نمط الطلب/الاستجابة (Request/Response Pattern):

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

ولنذكر مثال من نظام الإدارة المالية (Finance Service) حيث ترسل طلباً إلى نظام إدارة المواعيد (Appointment Management) للحصول على تفاصيل المواعيد التي تحتاج إلى فواتير.

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

التواصل غير المتزامن المعتمد على الأحداث (Event-Based, Asynchronous Communication):

يتم إرسال واستقبال البيانات في هذا النمط بين الخدمات باستخدام أحداث (Events) يتم نشرها إلى وسيط رسائل (Message Broker). الخدمة التي تنشئ الحدث (Producer) لا تنتظر استجابة فورية، مما يسمح للخدمات بالعمل بشكل مستقل.

مثال من نظام إدارة المختبرات (Lab Tests) حينما يتم الانتهاء من تحليل، تنشر خدمة المختبر حدثاً (Event) مثل "LabTestCompleted"، يمكن لخدمة إدارة الأطباء (Doctor Management) الاشتراك فيه لتحديث حالة المريض.

هذا النمط يقلل الترابط بين الخدمات، ولكنه يزيد من التعقيد بسبب الحاجة إلى إدارة نظام الرسائل والأحداث مثل  (RabbitMQ, Kafka, and Redis).

إذن، ما الذي تختاره؟

كل طريقة لها مزاياها وتحدياتها، ويمكن استخدام كلاهما بناءً على سيناريو مختلف، على سبيل المثال:

  • نمط الطلب/الاستجابة مناسب للعمليات التي تحتاج إلى استجابة فورية مثل الحصول على تفاصيل موعد.
  • التواصل غير المتزامن مثالي لإشعارات النظام أو العمليات التي لا تحتاج إلى استجابة فورية مثل تحديث حالة الغرفة أو نشر نتائج التحاليل.

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

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