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

ينبغي للفريق أن يفصل الواجهة الخلفية إلى خدمات مصغرة عندما تصبح قاعدة شيفرة واحدة عائقًا أمام سرعة النشر أو التوسع أو الملكية. يمكن أن تساعد Rust وGo عندما نصل إلى تلك النقطة، لكن اختيار اللغة لا يمكنه إصلاح الحدود الضعيفة، أو ملكية البيانات الغامضة، أو شبكة مليئة بواجهات برمجة التطبيقات الثرثارة.
تضيف الخدمات المصغرة العمليات، وقفزات الشبكة، ومسارات النشر، واحتياجات المراقبة، وأنماط الفشل. وتبقي الخدمة الواحدة هذه التكاليف منخفضة بينما يتعلم فريق المنتج نطاقه. تحصل على وحدة نشر واحدة، ونموذج بيانات واحد، وحدود معاملة واحدة، ومكان واحد لفحص السجلات.
غالبًا ما تحتاج الواجهة الخلفية الصغيرة إلى هذه البساطة أكثر مما تحتاج إلى التوسع المستقل. إذا كان فريق واحد يملك المنتج، فإن البنية الأحادية المعيارية يمكن أن تمنح المطورين حدود حزم واضحة من دون إجبار كل ميزة على المرور عبر HTTP، والطوابير، واكتشاف الخدمات، والتتبع الموزع.
يبدأ التفكيك في أن يبدو منطقيًا عندما تتطابق حدود الخدمة مع الحدود التجارية. غالبًا ما تحتاج المدفوعات، والهوية، والبحث، ومعالجة الوسائط، والتحليلات إلى قواعد توسع منفصلة. قد تحتاج واجهة برمجة تطبيقات الدفع إلى زمن استجابة منخفض واتساق صارم. وقد تتسامح خط أنابيب التقارير مع التأخير وتفضّل الإنتاجية.
ينبغي للفرق أن تبدأ بالبيانات. تملك الخدمة المصغرة بياناتها، وتغييرات مخططها، وثوابتها. إذا كانت خدمتان تكتبان الصفوف نفسها، فقد بنى المطورون بنية أحادية موزعة مع نقاط فشل إضافية. وإذا استطاعت خدمة واحدة تغيير مخططها من دون تنسيق إصدار عبر الشركة كلها، فإن للحدود قيمة.
يشكل الاتساق التصميم. يمكن للبنية الأحادية أن تعتمد على معاملات قاعدة البيانات. أما نظام الخدمات المصغرة فيحتاج إلى خيارات صريحة: استدعاءات متزامنة لعمليات القراءة التي تحتاج بيانات حديثة، وأحداث لسير العمل التي يمكن أن تتأخر، ومفاتيح idempotency للأوامر التي قد يعيد العملاء المحاولة بشأنها.
على سبيل المثال، يمكن لخدمة الطلبات أن تقبل طلب إتمام الشراء، وتخزن الطلب، ثم تنشر حدثًا. ويمكن لخدمة التنفيذ أن تستهلك ذلك الحدث وتحجز المخزون. إذا فشلت خطوة التنفيذ، فستحتاج خدمة الطلبات إلى آلة حالات واضحة، مثل pending أو confirmed أو failed أو canceled. يحتاج المطورون إلى تصميم هذه الحالات مسبقًا لأن لا معاملة مشتركة ستنقذ سير العمل.
يحمل تصميم واجهة برمجة التطبيقات العبء نفسه. تحتاج الفرق إلى عقود مستقرة، وحمولات ذات إصدارات، ورموز خطأ واضحة، ومهلات زمنية تتوافق مع مسار المنتج. يمكن لبوابة API مكتوبة بـGo أن توجه الحركة وتعالج التوازي باستخدام goroutines. ويمكن لعامل Rust أن يعالج المسارات الساخنة حيث تهم سلامة الذاكرة وكلفة المعالج. تعمل هذه الخيارات على أفضل وجه بعد أن يحدد الفريق الحد الفاصل.

تلائم Go كثيرًا من خدمات الحافة لأن المطورين يمكنهم شحن خوادم HTTP مقروءة باستخدام المكتبة القياسية والموجهات الشائعة. تمنح اللغة الفرق بناءات سريعة، وقطع نشر بسيطة، ونموذج تزامن ينسجم جيدًا مع معالجة الطلبات. تمنح وثائق Go الرسمية الفرق ما يكفي من الأدوات الأساسية لبناء خدمات شبكية من دون طبقة إطار عمل كبيرة.
تلائم Rust الخدمات التي تحتاج إلى تحكم دقيق في الذاكرة، وسباقات البيانات، وزمن الاستجابة. وغالبًا ما تختارها الفرق لخوادم الذاكرة المخبئية، والمحللات، ومعالجات التدفق، والمكونات الأخرى التي تقع على المسار الساخن. يمنح مشروع Rust المطورين قواعد الملكية والاستعارة التي تلتقط فئات من أخطاء الذاكرة قبل وصول الشيفرة إلى الإنتاج.
يمكن لخادم API JSON مكتوب بـRust أن يستخدم التحليل من دون نسخ وI/O غير متزامن لتقليل ضغط التخصيص. قد يساعد ذلك نقطة نهاية تتعامل مع حمولات كبيرة أو حجم طلبات مرتفع. وقد يبطئ الاختيار نفسه فريقًا ما يزال يغير أشكال طلباته كل أسبوع. تطلب Rust من المطورين نمذجة الملكية بعناية. وتؤتي تلك الكلفة ثمارها عندما يكون المجال قد استقر.
يمكن لبوابة Go أن تكون الواجهة الأمامية لعدة خدمات وتفرض حدود الطلبات، والمصادقة، وقواعد التوجيه. يفيد هذا النمط عندما يحتاج العملاء إلى نقطة دخول واحدة وتحتاج فرق الخلفية إلى مساحة لتغيير الخدمات الداخلية. وقد يضر إذا تحولت البوابة إلى تطبيق ثانٍ يكرر منطق الأعمال من كل خدمة.
يحافظ أقوى نمط لواجهة برمجة التطبيقات على ضيق الأوامر ووضوح عمليات القراءة. يجب أن يقبل endpoint للأوامر مثل POST /orders نية واحدة ويعيد نتيجة دائمة. يجب أن يعرض endpoint للقراءة مثل GET /orders/{id} الحالة التي يحتاجها العملاء من دون أن يضطر العميل إلى استدعاء خمس خدمات. وإذا كانت الصفحة تحتاج إلى عرض مركب، فيمكن لواجهة backend-for-frontend أو خدمة استعلام أن تجمعه.
تحتاج الأحداث إلى الانضباط نفسه. ينبغي للفريق أن ينشر حقائق حدثت، مثل OrderCreated أو PaymentCaptured، وأن يحافظ على ثبات مخططات الأحداث. ينبغي للمستهلكين التعامل مع التكرار والترتيب غير المتسق في التسليم. وينبغي للمنتجين استخدام نمط outbox حتى لا ينفصل تنفيذ كتابة قاعدة البيانات عن نشر الحدث.
للتوسع أيضًا طبقات. يمكن لخدمة واحدة أن تتوسع عبر التخزين المؤقت، ونسخ القراءة، وعمال مدعومين بالطوابير، وفهارس أفضل. تكلف هذه التحركات أقل من تفكيك مجال باكرًا جدًا. ينبغي للفريق أن يستنفد الخيارات الرخيصة قبل أن يقبل الفشل الموزع كضريبة دائمة.
تساعد الخدمات المصغرة عندما تحتاج الفرق إلى إيقاع إصدار منفصل. يمكن لفريق البحث نشر تغييرات الترتيب من دون لمس الدفع. ويمكن لفريق المنصة ترقيع الهوية من دون مطالبة فرق المنتج بإعادة بناء تطبيقاتها. تصبح الملكية مرئية في الشيفرة، ولوحات المعلومات، والتنبيهات، والاستجابة للحوادث.
لكن تلك الملكية سلاح ذو حدين. إذ يحمل مالك الخدمة الآن مسؤولية المناوبة والتنبيه، وتخطيط السعة، وترحيلات المخطط، ودعم API، وأعمال الإيقاف التدريجي. إذا لم يستطع أي فريق تحمل هذه المهام، فسيتآكل حد الخدمة. ستنمو سلاسل الاستدعاء، وستتراكم المهلات، وسيحصل المطورون على أعطال الإنتاج عبر قراءة السجلات من خمس عمليات نشر.
ينبغي للمراقبة أن تسبق التفكيك. يحتاج المطورون إلى معرفات الطلبات، وسجلات منظمة، ومقاييس، وتتبع، وأهداف مستوى الخدمة قبل إضافة حدود شبكية. من دون هذه الأدوات، يمكن لمكوّن بطيء واحد أن يحول حادثًا روتينيًا إلى تخمين.

تبدأ المسار العملي ببنية أحادية معيارية. ضع المدفوعات، والحسابات، والطلبات، والإشعارات في وحدات منفصلة. امنع الوصول المباشر إلى قاعدة البيانات عبر حدود الوحدات. استخدم واجهات تشبه واجهات API المستقبلية. أضف اختبارات حول العقود. وعندما تحتاج إحدى الوحدات إلى نشرها أو تخزينها أو نمط توسع خاص بها، افصلها بأقل قدر من الدراما.
تصبح Rust وGo بعد ذلك أدوات لمهام محددة. استخدم Go حيث تهم سرعة الفريق، وخدمات HTTP، والبساطة التشغيلية. واستخدم Rust حيث يحتاج المسار الساخن إلى سلامة الذاكرة، أو تحكم محكم في الموارد، أو أداء متوقع. ولا تستخدم اللغتين معًا إلا عندما يستطيع الفريق دعم سلسلتي الأدوات كلتيهما، ومسارات التوظيف، وأنظمة البناء، ودفاتر تشغيل الحوادث.
ينمو الـbackend جيدًا عندما يفصله المطورون بسبب ضغط يمكنهم تسميته. ينبغي لحد الخدمة أن يقلل التنسيق، أو يعزل التوسع، أو يحمي ثابتا حرجًا. إذا لم يفعل أيًا من ذلك، فأبقِ الشيفرة معًا واجعل حد الوحدة أكثر إحكامًا.

التعليقات
يرجى تسجيل الدخول أو إنشاء حساب للانضمام إلى النقاش