تصميم واجهات برمجية للخدمات المصغّرة
تصميم واجهات برمجية للخدمات المصغّرة
حين تنشر خدمة مصغّرة واجهةً برمجية فهي لا تكتب كودًا فحسب — بل تنشر عقدًا. كل فريق يعتمد على تلك الخدمة يثق في ثبات هذا العقد. كسره في صمت هو أسرع طريقة لإسقاط نظام موزّع في الساعة الثانية فجرًا. يتناول هذا الدرس كيفية تعريف العقود بدقة، وتطويرها دون التسبّب في توقف الخدمة، والتفكير في التوافق مع الإصدارات السابقة باعتباره اهتمامًا هندسيًا من الدرجة الأولى.
ماهية عقد واجهة برمجية التطبيقات
عقد واجهة برمجية التطبيقات هو الوصف الكامل والقابل للمعالجة آليًا لما تقبله الخدمة وما تُعيده: بنية URL وأساليب HTTP وهيئات الطلبات وأشكال الاستجابات ورموز الحالة وتنسيقات الأخطاء ومتطلبات المصادقة ورؤوس حدود المعدل. العقود أكثر من مجرد توثيق — فهي مرجع الاختبارات وبوابة النشر.
في بيئة Spring Boot 3 المعيار الفعلي لعقود REST هو OpenAPI 3 (المعروف سابقًا بـ Swagger). أضف springdoc-openapi-starter-webmvc-ui إلى خدمتك وسيُولّد المواصفة تلقائيًا من تعليقاتك التوضيحية:
تُصبح مواصفة JSON المُولَّدة على /v3/api-docs هي أثر العقد. تنشرها في سجل المخططات (Confluent أو AWS Glue أو مستودع Git عادي)، ويربط فرق المستهلكين اختبارات التكامل الخاصة بهم بها.
استراتيجيات إصدار الواجهات البرمجية
جميع الواجهات البرمجية تتغيّر. السؤال الوحيد هو كيف تعرض هذا التغيير للمستهلكين. هناك أربع استراتيجيات سائدة لكل منها مقايضاتها:
- الإصدار عبر URI —
/api/v1/orders،/api/v2/orders. بسيط وواضح للغاية وسهل التوجيه في بوابة API. الجانب السلبي: تصون N من أشجار المسارات الموازية ويجب أن يختار المستهلكون صراحةً. - الإصدار عبر الرأس —
Accept: application/vnd.myapp.v2+jsonأوX-API-Version: 2. يُبقي عناوين URL نظيفة؛ أصعب في الاختبار عبر متصفح أو المشاركة كرابط. - الإصدار عبر معامل الاستعلام —
/api/orders?version=2. قابل للاكتشاف بسهولة لكن الإصدارات معادية للتخزين المؤقت وتُلوّث عناوين URL. - التفاوض على المحتوى (نوع الوسيط). الأصح نظريًا لـ REST، الأكثر ألمًا تشغيليًا. نادرًا ما يُستخدم خارج واجهات برمجية المنصات الكبيرة.
للخدمات المصغّرة خلف Spring Cloud Gateway يكون النهج الأكثر قابليةً للصيانة هو الإصدار عبر URI مقترنًا بتوجيه البوابة. تُعيّن البوابة /api/v2/** إلى نشر خدمة جديد بينما يواصل /api/v1/** خدمة القديم:
/api/v1 بأثر رجعي إلى خدمة حية شُحنت كـ /api/orders يعني تنسيق تغيير كاسر عبر كل مستهلك في آنٍ واحد. إضافة /v1 مبكرًا لا تكلف شيئًا وتمنحك مرونة لا محدودة في المستقبل.
قواعد التوافق مع الإصدارات السابقة
التوافق مع الإصدارات السابقة يعني أن المستهلكين الحاليين يستمرون في العمل دون أي تغيير من جانبهم. القواعد سهلة الصياغة لكن سهلة الانتهاك بشكل غير مقصود:
التغييرات الآمنة (غير الكاسرة):
- إضافة حقل اختياري جديد إلى هيئة الاستجابة.
- إضافة معامل استعلام اختياري جديد.
- إضافة نقطة نهاية جديدة (مسار جديد، نفس الإصدار).
- تخفيف قيد تحقق (قبول قيم أكثر مما كان سابقًا).
- إضافة قيمة تعداد جديدة — فقط إن كان المستهلكون يتعاملون مع القيم غير المعروفة بصورة صحيحة.
التغييرات الكاسرة (تستلزم إصدارًا جديدًا):
- حذف حقل أو إعادة تسميته.
- تغيير نوع الحقل (مثلًا من
intإلىstring). - جعل حقل اختياري إلزاميًا.
- تغيير دلالة حقل موجود دون إعادة تسميته.
- حذف نقطة نهاية.
- تغيير متطلبات المصادقة/التفويض.
طبّق هذا تلقائيًا. في CI، استخدم أداةً كـ openapi-diff أو Optic لمقارنة المواصفة الجديدة بخط الأساس المنشور وأفشل عملية البناء عند التغييرات الكاسرة:
التصميم للمستهلكين: القارئ المتسامح وقانون بوستل
تتطلّب المتانة في النظام الموزّع أن يكون العملاء قرّاءً متسامحين: يجب أن يتجاهلوا الحقول التي لا يعرفونها وألّا ينهاروا أمام قيم تعداد غير معروفة. هذا هو قانون بوستل مطبّقًا على واجهات JSON البرمجية: "كن محافظًا فيما ترسل، متسامحًا فيما تقبل."
في Jackson (الذي يستخدمه Spring Boot) اضبط التسامح عالميًا حتى لا تكسر الحقول الجديدة في الاستجابة العملاءَ القدامى:
إصدار الواجهات البرمجية الداخلية مقابل الخارجية
لا تحتاج كل واجهة برمجية إلى نفس انضباط الإصدار. طبّق نهجًا قائمًا على المخاطر:
- واجهات برمجية بين خدمات داخلية: أنت تملك كل المستهلكين ويمكنك تنسيق التغييرات. تقبّل مزيدًا من التقلّب؛ استخدم علامات الميزات وانشر المستهلكين أولًا (نمط التوسيع والتقليص).
- الواجهات البرمجية التي يستهلكها تطبيق جوّال: لا يمكنك إجبار المستخدم على الترقية. عامِلها كواجهات برمجية عامة؛ احتفظ بالإصدارات القديمة لمدة 12-24 شهرًا وأوقفها مع إشعار مسبق.
- الواجهات البرمجية المكشوفة عبر بوابة API لمطوّرين خارجيين: أكثر صرامة. انشر سياسة إهمال (90 يومًا على الأقل)، ورأس Sunset، ودليل الترحيل قبل حذف أي شيء.
يُسهّل Spring Boot إرسال رأسَي Deprecation وSunset على نقاط النهاية القديمة باستخدام فلتر أو استجابة نصيحة:
تصميم عقد الأخطاء
الأخطاء جزء من العقد أيضًا. استجابات الأخطاء العشوائية ({"error": "something went wrong"}) تُجبر كل مستهلك على كتابة منطق تحليل خاص. استخدم RFC 9457 Problem Details (application/problem+json) الذي يدعمه Spring Framework 6 أصلًا عبر ProblemDetail:
شكل خطأ موحّد يعني أن فرق المستهلكين يمكنها كتابة مكتبة معالجة أخطاء واحدة وإعادة استخدامها عبر جميع الخدمات في المنصة.
الخلاصة
عقود الواجهات البرمجية هي الجدران الحاملة في معمارية الخدمات المصغّرة. عرّفها بدقة باستخدام OpenAPI 3، وضع إصداراتها من البداية عبر الإصدار في URI، وأتمت فحص التوافق مع الإصدارات السابقة في CI، وقيّس أشكال الأخطاء مع RFC 9457 Problem Details. اكتب العملاء كقرّاء متسامحين حتى لا تتسبّب التغييرات الآمنة على جانب المزوّد في حدوث فشل متسلسل. هذه العادات هي ما يميّز منصةً قابلة للصيانة عن مونوليث موزّع يُمسك ببعضه بفعل الخوف من التغيير.