المعمارية الأحادية مقابل الخدمات المصغّرة
المعمارية الأحادية مقابل الخدمات المصغّرة
كل نظام برمجي يبدأ من مكان ما. معظمها يبدأ بمعمارية أحادية — وحدة نشر واحدة تحتوي كل الميزات في نفس العملية. الخدمات المصغّرة هي استجابة مدروسة للمشكلات التي تنشأ في المعماريات الأحادية عند التوسّع. يدرس هذا الدرس كلتا المعماريتين بموضوعية، لأن أهم مهارة تحتاجها قبل تبنّي الخدمات المصغّرة هي معرفة متى لا تفعل ذلك.
ما هي المعمارية الأحادية فعلًا
المعمارية الأحادية ليست مرادفًا لـ"الكود السيئ". هي طوبولوجيا نشر: نتيجة بناء واحدة (.jar أو .war أو حاوية) تضمّ التطبيق بأكمله. تطبيقات Spring Boot هي معماريات أحادية بشكل افتراضي. عند تشغيل mvn spring-boot:run، تبدأ جميع وحدات التحكم والخدمات والمستودعات ومنطق النطاق في JVM واحدة.
تأتي المعماريات الأحادية بأشكال مختلفة. المعمارية الأحادية المنظّمة بوحدات تفرض حدودًا واضحة بين الحزم وقواعد التبعية داخل نتيجة نشر واحدة. أما كرة الطين الكبيرة فلا تفرض أي هيكل. النوع الثاني هو ما يقصده الناس عادةً حين يشتكون من المعماريات الأحادية — لكن المعمارية ذاتها ليست سبب الفوضى؛ السبب هو غياب الانضباط.
المشكلات التي تدفع الفرق نحو الخدمات المصغّرة
تطوّر المعماريات الأحادية نقاط ألم متوقّعة مع نموّ قاعدة الكود وتوسّع الفرق. فهم هذه النقاط يُخبرك ما إذا كانت الخدمات المصغّرة هي العلاج الصحيح فعلًا.
- اقتران النشر. خطأ في وحدة المدفوعات يجبرك على إعادة نشر التطبيق بأكمله — بما في ذلك وحدة ملف المستخدم غير ذات الصلة. تنمو المخاطر ونطاق التأثير مع حجم كل إصدار.
- عدم مرونة التوسّع. إذا كانت ميزة معالجة الصور فقط تحت ضغط، فلا يزال عليك توسيع التطبيق بأكمله. العملية الواحدة تعني وحدة توسّع واحدة.
- القيد التكنولوجي. يجب أن تستخدم كل أجزاء النظام نفس اللغة والبيئة وإصدارات التبعيات. الفريق الذي يريد تجربة قاعدة بيانات أو إطار عمل مختلف لا يستطيع ذلك دون التأثير على الجميع.
- اختناق استقلالية الفريق. حين يعمل عشرون مطوّرًا في قاعدة كود واحدة، تصبح تعارضات الدمج وملكية مجموعة الاختبارات وتنسيق الإصدار المشترك عبئًا كبيرًا.
- اقتران التوفّر. تسريب في الذاكرة أو استثناء غير مُعالَج في جزء من العملية قد يُعطّل التطبيق بأكمله.
ما الذي تُقايضه الخدمات المصغّرة بتلك الحلول
تُجزّئ الخدمات المصغّرة التطبيق إلى خدمات قابلة للنشر باستقلالية، كل منها تمتلك بياناتها الخاصة وتتواصل عبر الشبكة. يمنحك النظام البيئي لـ Spring لبنات البناء: Spring Boot لكل خدمة، وSpring Cloud للاهتمامات المشتركة، وسجل خدمة كـ Eureka أو Consul للاكتشاف.
هيكل بسيط بخدمتين يوضّح الشكل. تستدعي order-service الخدمةَ inventory-service عبر HTTP:
ما يبدو نظيفًا في المخطط يحمل تكاليف حقيقية لأنظمة موزّعة:
- تأخر الشبكة والفشل الجزئي. استدعاء HTTP إلى
inventory-serviceقد ينتهي بمهلة انتظار، أو يُعيد 503، أو لا يصل أبدًا. في المعمارية الأحادية، استدعاء خدمة هو استدعاء دالة — شبه مجاني ومتزامن دائمًا. - تتبّع التوزيع. طلب مستخدم واحد يمتد الآن على عمليات متعددة. بدون معرّف ارتباط يُنقل في الترويسات وأداة كـ Zipkin أو OpenTelemetry، يصبح التنقيح تخمينًا.
- اتساق البيانات. لا يمكنك تغليف فحص المخزون وإدراج الطلب في معاملة قاعدة بيانات واحدة. تحتاج أنماط اتساق مؤجّل (sagas، outbox) غير موجودة أصلًا في المعمارية الأحادية.
- المساحة التشغيلية. عشر خدمات تعني عشر خطوط CI/CD، وعشر صور حاويات، وعشر نقاط صحة للمراقبة، وعشر مجموعات أسرار للتدوير، وعشرة أماكن لانحراف الإعدادات.
البُعد الأمني
يتغيّر الأمان جوهريًا حين تُجزّئ المعمارية الأحادية. في المعمارية الأحادية، تُطبّق Spring Security المصادقة مرة واحدة عند الحافة، وكل استدعاء لاحق موثوق ضمنيًا. في نظام الخدمات المصغّرة، كل استدعاء بين الخدمات يعبر حدود الشبكة وهو ثغرة أمنية محتملة.
الإجابة المعيارية هي توزيع JWT: تُتحقّق بوابة API من الرمز الوارد، ثم تُمرّره (أو رمز حساب خدمة) إلى الخدمات المصبّة. تتحقّق كل خدمة بشكل مستقل من توقيع الرمز.
يجب الآن إدارة تدوير المفاتيح وانتهاء صلاحية الرموز وثقة الخدمة بالخدمة عبر كل خدمة — تعقيد غائب تمامًا في المعمارية الأحادية حيث تُغطّي SecurityFilterChain واحدة كل شيء.
متى تستحق الخدمات المصغّرة العناء — ومتى لا
استخدم هذه القائمة كمرجع عملي قبل الالتزام بالتقسيم:
- حجم الفريق واستقلاليته أهم من حجم الكود. إذا احتاج فريقان لنشر نفس الوحدة بجداول زمنية مختلفة دون تنسيق، فهذه حالة استخدام حقيقية للخدمات المصغّرة. إذا كان فريق واحد يمتلك كل شيء، فالمعمارية الأحادية المنظّمة بوحدات تمنح معظم الفوائد بجزء بسيط من التكلفة.
- ابدأ بمعمارية أحادية، استخرج لاحقًا. لا تعرف تقريبًا حدود الخدمة الصحيحة مسبقًا. التقسيم المبكر يخلق حدودًا خاطئة مكلفة التراجع عنها بعد أن تمتلك الخدمات قواعد بيانات وفرقًا منفصلة.
- متطلبات التوسّع المستقلة إشارة قوية. إذا احتاج جزء من نظامك عشرة أضعاف قدرة الحوسبة مقارنة بجزء آخر، ويمكن فصل هذين الجزأين بوضوح، فهذا سبب حقيقي للتقسيم.
- النضج التنظيمي مهم. الخدمات المصغّرة تتطلّب قدرة DevOps: نشر آلي، وتنسيق الحاويات، وتسجيل مركزي، وتتبّع موزّع، وشبكة خدمات أو mTLS. فريق بدون هذه الممارسات سيغرق في العبء التشغيلي.
نقطة البداية العملية
إذا كنت تبني نظامًا جديدًا، ابدأ من هنا: هيكل تطبيق Spring Boot الخاص بك كمعمارية أحادية منظّمة بوحدات مع حدود حزم واضحة (com.example.orders، com.example.inventory). حدّد واجهات صريحة بين الوحدات. تجنّب الوصول المباشر إلى الحقول عبر الوحدات. عندما تتباين متطلبات نشر وحدة ما أو توسّعها فعلًا — ويمكنك ملاحظة ذلك التباين في بيانات الإنتاج — استخرجها كخدمة منفصلة في تلك النقطة.
هذا النهج يمنحك مسارًا واضحًا للاستخراج لأنك فكّرت مسبقًا في الحدود، دون دفع الضريبة التشغيلية للخدمات المصغّرة قبل الحاجة.
الخلاصة
المعماريات الأحادية والخدمات المصغّرة هي طوبولوجيات نشر، وليست مستويات جودة. المعمارية الأحادية أبسط في البناء والاختبار والتنقيح والأمان. الخدمات المصغّرة تحلّ مشكلات حقيقية — النشر المستقل والتوسّع الدقيق واستقلالية الفريق — لكنها تُدخل تعقيد الأنظمة الموزّعة وأنماط فشل الشبكة والعبء التشغيلي الكبير. المعمارية الصحيحة هي التي تناسب حجم فريقك وإيقاع نشرك ونضجك التشغيلي. لمعظم الفرق في معظم المراحل، يعني ذلك البدء بمعمارية أحادية منظّمة جيدًا والانتقال انتقائيًا، لا إعادة كتابة كل شيء كخدمات من اليوم الأول.