من التطبيق الأحادي إلى الخدمات المصغّرة: متى تُقدِم على الانتقال
الخدمات المصغّرة ليست وسام نضج. إليك كيف تعرف متى يكون التقسيم مجديًا فعلاً، وكم يكلّف حقًا، وكيف تهاجر دون أن تكسر كل شيء.
بقلم Innovation T Team
في مرحلة ما، أصبحت عبارة «نحن ننتقل إلى الخدمات المصغّرة» وسيلة للإشارة إلى أن الفريق قد نضج. نادرًا ما تسير الأمور بهذه السلاسة. فقد حطّم كثير من الفرق تطبيقًا سليمًا تمامًا إلى عشرات الخدمات، وضاعفوا حِملهم التشغيلي ثلاث مرات، ثم اكتشفوا أن المشكلة الأصلية لم تكن يومًا في البنية المعمارية على الإطلاق. السؤال الصادق ليس «أحادي أم خدمات مصغّرة»، بل «ما الذي نحاول حلّه فعلاً، وهل النظام الموزّع هو أرخص طريقة لحلّه».
يدعو هذا الدليل إلى خيار افتراضي مملّ (ابدأ بتطبيق أحادي جيّد البنية)، ثم يعرض الإشارات التي تجعل التقسيم مجديًا، والتكاليف التي لا يحذّرك منها أحد، ونمط هجرة يتيح لك التحرّك دون إعادة كتابة محفوفة بالمخاطر.
ابدأ بتطبيق أحادي معياري
بالنسبة لمعظم المنتجات، تكون البنية الأولى الصحيحة تطبيقًا واحدًا قابلًا للنشر ذا حدود داخلية نظيفة. هذا هو التطبيق الأحادي المعياري، وهو ليس حلًّا وسطًا، بل خيار مشروع على المدى الطويل.
داخله، يظل الكود منظّمًا حول مجالات الأعمال: الفوترة، والكتالوج، والحسابات، والإشعارات. تملك كل وحدة (module) بياناتها الخاصة وتعرض واجهة واضحة للوحدات الأخرى، لكن الوحدات تتخاطب عبر استدعاءات دوال داخل العملية نفسها، لا عبر طلبات شبكية. تحصل بذلك على معظم انضباط التصميم الخاص بالخدمات المصغّرة دون أن تدفع تقريبًا أي ضريبة تشغيلية.
المزايا حقيقية. قاعدة كود واحدة تعني بناءً واحدًا، ونشرًا واحدًا، ومكانًا واحدًا تنظر إليه حين يتعطّل شيء. استدعاء الدالة إمّا ينجح وإمّا يطرح استثناءً، لذا لن تضطر أبدًا إلى تنقيح سير عمل نصف مكتمل مبعثر عبر ثلاث خدمات. إعادة الهيكلة عبر الحدود مجرّد إعادة تسمية بمساعدة المترجِم (compiler)، والمعاملات بسيطة لأن كل شيء يتشارك قاعدة بيانات واحدة.
والأهم أن التطبيق الأحادي المعياري النظيف يُبقي خياراتك مفتوحة. حين ترسم حدودًا حادّة بين الوحدات مبكرًا، يمكن انتزاع أيٍّ منها لاحقًا إلى خدمتها الخاصة بألم أقل بكثير. الانضباط الذي تستثمره في حدود الوحدات هو عين الانضباط الذي تتطلبه الخدمات المصغّرة، فلا يذهب أيٌّ منه سُدى. أنت لا تختار التطبيق الأحادي إلى الأبد، بل تختار تأجيل التوزيع حتى يتوفّر لديك سبب ملموس له.
الإشارات التي تبرّر التقسيم فعلاً
يُبرَّر التقسيمُ بالضغط التنظيمي والتشغيلي، لا بالاعتبارات الجمالية. يُفضّل أن ترى أكثر من واحدة من هذه الإشارات قبل أن تلتزم.
توسّع الفرق وتكلفة التنسيق. أدوم سبب للتقسيم هو البشر. حين تلتزم عدة فرق بالمساهمة في قاعدة الكود نفسها وتتنازع على خط أنابيب النشر نفسه، يصبح كل إصدار مفاوضةً، وتحجب ميزةٌ غير مكتملة لفريقٍ إصلاحًا عاجلًا لفريق آخر. تتيح الخدمات لكل فريق أن يملك جزءه وينشره ويوسّعه وفق جدوله الخاص. إن كان لديك فريق أو فريقان، فهذا الضغط غير موجود بعد، ولن يفيدك التقسيم.
الحاجة إلى نشر مستقل ومتكرر. حين يُطلَق جزء من النظام عدة مرات يوميًا بينما يتغيّر جزء آخر مرة كل ربع سنة، فإن جمعهما في نشرة واحدة يخلق احتكاكًا. وحدة المدفوعات التي يجب ألا تتراجع أبدًا لا تريد أن يُعاد نشرها كلما تغيّر نص تسويقي. هذه فائدة حقيقية، لكن فقط إذا كانت استراتيجيتك في الاختبار والتراجع قادرة على التعامل مع كثير من عمليات النشر الصغيرة بدلًا من قليل من الكبيرة.
احتياجات توسّع متباينة فعلاً. أحيانًا يكون أحد المكوّنات نهِمًا لوحدة المعالجة المركزية (CPU) ويقفز إلى أضعاف حِمله المعتاد أثناء تخفيضات، بينما يبقى بقية التطبيق خاملًا. إن لم تستطع توسيع هذا المسار الساخن دون أن تدفع لتوسيع كل ما حوله، فقد يستحق عزله كخدمة مستقلة العناء. كن صادقًا هنا: تحلّ خوادم التطبيقات الحديثة وطبقة تخزين مؤقت (cache) جيدة كثيرًا من مشكلات التوسّع دون أي تقسيم على الإطلاق. لا يستحق التقسيم لتوفير موارد الحوسبة العناء إلا حين يكون التباين كبيرًا ومستمرًا.
عزل الأعطال المستقل. إذا كان تكامل خارجي بطيء أو مهمة خلفية متفلّتة قادرًا على إسقاط تطبيقك بأكمله، فإن سحب هذا العمل الخطر إلى خدمته الخاصة قد يحتوي نطاق الانفجار، لا سيّما حين تكون لأجزاء النظام متطلبات موثوقية شديدة الاختلاف.
لاحظ ما ليس مدرجًا في هذه القائمة: «قاعدة الكود تبدو ضخمة»، أو «نريد لغة مختلفة لميزة واحدة»، أو «قالت محاضرة في مؤتمر إن التطبيقات الأحادية لا تتوسّع». هذه ليست أسبابًا، بل حالات مزاجية.
التكاليف التي لا يضعها أحد في العرض التقديمي
كل فائدة من تلك الفوائد تُشترى بتكلفة حقيقية ومتكررة. قبل أن تقسّم، أدرِج هذه التكاليف في حسابك.
الشبكة الآن في مسارك الحرِج. في التطبيق الأحادي، يستغرق الاستدعاء بين الوحدات نانوثوانٍ ولا يمكن أن يفشل بمفرده. أما بين الخدمات، فيقطع الاستدعاء نفسه شبكةً بطيئة وغير موثوقة ومتوقفة أحيانًا. صار عليك الآن معالجة المُهَل، وإعادة المحاولات، والإخفاقات الجزئية، والانقطاعات المتتالية. الطلب الذي يمرّ بست خدمات يراكم ست فرص للفشل.
اتساق البيانات يصبح صعبًا. قاعدة بيانات واحدة تمنحك المعاملات مجانًا. وما إن تمتلك كل خدمة مخزن بياناتها الخاص حتى تفقد القدرة على تحديث عدة أشياء بشكل ذرّي، وتنتقل إلى عالم الاتساق النهائي (eventual consistency)، والساغا (sagas)، والإجراءات التعويضية. جعل الطلب وعدّاد المخزون والدفعة متوافقة عبر ثلاث خدمات دون معاملة مشتركة هو أحد المشكلات الصعبة حقًا في الأنظمة الموزّعة، ومن السهل الوقوع في خطأ دقيق فيه.
قابلية المراقبة تصبح مشروعًا، لا ملف log. في التطبيق الأحادي، يروي أثر المكدّس (stack trace) القصة كاملة. أما في النظام الموزّع، فيترك فعلٌ واحد للمستخدم آثارًا عبر خدمات وأجهزة عديدة. من دون تتبّع موزّع (distributed tracing)، وتسجيل مركزي، ومعرّفات ربط (correlation IDs)، يتحوّل التنقيح إلى تنقيب أثري. عليك بناء هذه الطبقة قبل أن تحتاجها، لا بعد أول حادث في الساعة الثانية فجرًا.
الأعباء التشغيلية تتضاعف. تحتاج كل خدمة إلى خط أنابيبها الخاص، ونشرها، ومراقبتها، وتنبيهاتها، وأسرارها، ونوبة استعدادها. صار التطوير المحلي يعني تشغيل أسطول من الخدمات أو محاكاته، وترتفع فاتورة الحوسبة السحابية. إن كنت متجهًا في هذا الاتجاه، فخطّط ميزانيتك بتأنٍّ: يشرح دليلنا لتحسين تكاليف الحوسبة السحابية كيف تمنع بصمة متعددة الخدمات من استنزاف رصيدك بهدوء.
العقود بين الخدمات تتكلّس. داخل التطبيق الأحادي، يمكنك تغيير واجهة مشتركة وترك المترجِم يعثر على كل مستدعٍ. أما بين خدمات تملكها فرق مختلفة، فيتحوّل التغيير نفسه إلى مفاوضة على API مُصدَّرة الإصدارات مع توافق خلفي يجب الحفاظ عليه. تصميم تلك الواجهات جيدًا مهم للغاية، لذا يجدر بك قراءة دليلنا عن كيفية تصميم واجهات API يحبّها المطوّرون قبل أن تجمّد أي حد في مكانه.
هاجر بنمط التينة الخانقة، لا بإعادة الكتابة
إذا كانت لديك إشارات صادقة وقد حسبت التكاليف، فلا توقف كل شيء من أجل إعادة كتابة. إعادة كتابة نظام يعمل هي الطريقة التي تخسر بها الشركات عامًا كاملًا دون أن تطلق شيئًا. استخدم نمط التينة الخانقة بدلًا من ذلك.
يأتي الاسم من نبتة متسلقة تنمو حول شجرة مضيفة، وتستولي تدريجيًا على بنيتها حتى يمكن إزالة الأصل. وتطبيقًا على البرمجيات، يعني ذلك أن تُنمّي البنية الجديدة حول القديمة، قطعة تلو الأخرى، بينما يستمر النظام في العمل.
عمليًا، تضع طبقة توجيه، عادةً بوابة API أو واجهة أمامية (facade)، أمام التطبيق الأحادي بحيث لا يعرف المستدعون ما وراءها ولا يعنيهم ذلك. تختار قدرة واحدة محدّدة الحدود جيدًا، وتنتزعها إلى خدمة جديدة، وتعيد توجيه حركتها بهدوء عبر البوابة. ينكمش التطبيق الأحادي بمسؤولية واحدة. تتحقق، وتستقر، وعندها فقط تختار القطعة التالية. وعبر خطوات صغيرة قابلة للعكس كثيرة، يُخنَق التطبيق الأحادي حتى العدم، أو حتى النواة التي يعقل الاحتفاظ بها.
قوة هذا النهج أن كل خطوة تُطلَق إلى الإنتاج ويمكن التراجع عنها وحدها. أنت لست أبدًا على بُعد انفجار كبير واحد من الكارثة، ويمكنك التوقف عند أي نقطة إذا تبيّن أن التطبيق الأحادي المتبقي على ما يرام تمامًا.
ارسم الحدود حول المجالات، لا الطبقات
القرار الأهم هو أين تمرّ الحدود، وأشيع خطأ هو القطع على طول الطبقات التقنية: «خدمة قاعدة بيانات»، «خدمة منطق أعمال»، «خدمة واجهة». هذا يخلق خدمات لا تستطيع فعل أي شيء مفيد بمفردها ويجب أن تثرثر باستمرار لإتمام أي مهمة حقيقية. تحصل على كامل تكلفة التوزيع وعلى لا شيء من الاستقلالية.
اقطع على طول مجالات الأعمال بدلًا من ذلك. يجب أن تملك الخدمة قدرة كاملة من طرف إلى طرف، بما في ذلك بياناتها الخاصة. «الطلب» و«الفوترة» و«المخزون» حدود جيدة لأن كلًّا منها يمثّل شيئًا تفعله المؤسسة فعلاً. الخدمة المرسومة جيدًا يمكنها تغيير دواخلها ومخططها ومنطقها دون استئذان، لأن لا شيء خارجها يعتمد عليها.
الحدّ الجيد هو حيث تكون التفاعلات ضخمة لا ثرثارة (بضعة استدعاءات ذات معنى، لا مئات صغيرة)، وحيث تتجمّع البيانات طبيعيًا على جانب واحد من الخط، وحيث يمكن لفريق واحد أن يملك الأمر كله. إذا كانت «خدمتان» تحدّثان بيانات بعضهما باستمرار في النَّفَس نفسه، فهما في الحقيقة خدمة واحدة قُسّمت في المكان الخطأ.
قائمة التحقق من الجاهزية
قبل أن تنتزع خدمتك الأولى، ينبغي أن تكون قادرًا على الإجابة بنعم عن معظم هذه النقاط.
- لديك إشارة ملموسة (توسّع الفرق، أو النشر المستقل، أو التوسّع المتباين)، لا مجرد إحساس بأن التطبيق الأحادي ضخم.
- تطبيقك الأحادي معياري بالفعل، بحدود واضحة يمكنك القطع على طولها.
- لديك تكامل وتسليم مستمران ناضجان، بحيث يكون إطلاق كثير من عمليات النشر الصغيرة أمرًا روتينيًا لا محفوفًا بالمخاطر.
- لديك تسجيل مركزي، ومقاييس، وتتبّع موزّع جاهز، أو خطة حازمة لإضافتها أولًا.
- لديك نهج بنية تحتية (حاويات، وتنسيق، وتزويد آلي) يجعل إنشاء خدمة جديدة رخيصًا.
- لديك خطة لاتساق البيانات عبر الخدمات، وتعرف أي مسارات عمل ستحتاج إلى اتساق نهائي.
- الفرق مُهيكَلة لتملك الخدمات من طرف إلى طرف، بما في ذلك بياناتها ونوبات استعدادها.
- حسبت التكلفة التشغيلية والسحابية المستمرة، والمؤسسة تقبلها.
إذا كانت معظم الإجابات لا، فإن العمل الأعلى مردودًا ليس التقسيم، بل ترتيب التطبيق الأحادي المعياري وبناء العضلات التشغيلية أولًا.
أنماط مضادة يجب تجنّبها
التطبيق الأحادي الموزّع. خدمات يجب نشرها كلها معًا، في تزامن تام، وتتشارك قاعدة بيانات، وتفشل ككيان واحد. دفعت كامل ثمن التوزيع واحتفظت بكل مساوئ التطبيق الأحادي. هذا أسوأ ما في العالمَين، وهو إلى حد بعيد أشيع الإخفاقات.
التقسيم لإصلاح جودة الكود. الكود السيّئ لا يتحسّن بتوزيعه عبر شبكة. المنطق المتشابك يصبح منطقًا متشابكًا مع زمن استجابة وإعادة محاولات مركّبة عليه. نظّف الكود داخل التطبيق الأحادي أولًا.
الخدمات المتناهية الصغر. حدود دقيقة إلى حدٍّ يجعل فعلًا واحدًا للمستخدم يتشعّب عبر عشرات الخدمات. تُغرِق أعباء التنسيق أي فائدة، ولا يستطيع أحد أن يستوعب النظام في ذهنه.
قاعدة بيانات مشتركة خلف خدمات عديدة. إذا كانت عدة خدمات تقرأ وتكتب في الجداول نفسها، فهي مرتبطة ببعضها ارتباطًا وثيقًا. تغيير مخطط واحد يكسر الجميع، ولا تملك من الاستقلالية إلا الاسم.
إعادة الكتابة بدلًا من الخنق. تجميد العمل على الميزات لإعادة بناء كل شيء من الصفر. يستمر النظام القديم في التطوّر بينما تعيد البناء، فيتحرّك الهدف، ويتجاوز المشروع حدوده. أنمِ الجديد حول القديم بدلًا من ذلك.
الخلاصة العملية
الخدمات المصغّرة أداة لمجموعة محدّدة من المشكلات، معظمها المقياس التنظيمي والنشر المستقل، وهي تحل تلك المشكلات بمقايضة البساطة بالاستقلالية. تستحق هذه المقايضة حين تتوفر لديك البشر، والضغط، والنضج التشغيلي لاستيعاب التكلفة. وهي مقايضة سيئة حين تلاحق موضة أو تغطّي كودًا فوضويًا. ابدأ معياريًا، وابقَ صادقًا بشأن إشاراتك، وهاجر بنمط التينة الخانقة، وارسم حدودك حول المجالات لا الطبقات.
إذا كنت توازن هذا القرار وتريد رأيًا ثانيًا واضح البصيرة، فهذا بالضبط نوع المشكلات التي نساعد الفرق على تجاوزها. استكشف خدماتنا في هندسة البرمجيات والحوسبة السحابية، أو تواصل معنا لمناقشة معماريتك مع فريق Innovation T.
جاهز للبناء مع Innovation T؟
سواء كان الأمر يتعلق بالأمن أو النمو أو الهندسة، يمكن لفريقنا مساعدتك على تنفيذه بإتقان.