Serverless في الإنتاج: دروس من منصة AWS مؤسسية

أكثر من خمسين دالة Lambda، معمارية سداسية، خطوط معالجة دفعات مدفوعة بـ SQS، واستعادة كوارث متعددة المناطق — البنية وتصميم الطوابير والقرارات التشغيلية خلف منصة serverless مؤسسية.

تعلمتُ أن أحترم المتطلبات التي تبدو مملة. «عملاء الشركات يرفعون ملفات CSV» تُقرأ كمشروع عطلة نهاية أسبوع — إلى أن يتوجّب التحقق من تلك الملفات مقابل بيانات مرجعية، وتسجيلها في أنظمة خلفية خارجية عبر SFTP، وإرجاع النتائج آنيًا، على منصة يجب أن تنجو من انقطاع منطقة كاملة بهدف استرجاع (RTO) يُقاس بالساعات، وأن تُرضي مدقّقًا يقرأ سجلات VPC Flow Logs الخاصة بك.

قُدتُ التسليم التقني لمنصة B2B لاستقبال البيانات بهذه المتطلبات بالضبط، مبنية بالكامل serverless على AWS. هذه المقالة جولة في القرارات المعمارية التي جعلتها تعمل — وتلك التي لم تتكشّف قيمتها إلا لاحقًا.

شكل النظام

  • أكثر من 50 دالة Lambda ‏(TypeScript، Node.js 22) موزعة بين طبقة API متزامنة ومعالجة دفعات غير متزامنة، تُبنى وتُنشر بـ AWS SAM
  • Aurora PostgreSQL عبر Prisma، وأمامها RDS Proxy
  • طوابير SQS تقود خط التحقق والتسجيل متعدد المراحل
  • تكامل SFTP مع أنظمة خارجية قديمة
  • WebSocket API يدفع حالة المعالجة إلى المتصفح آنيًا
  • React 19 SPA في monorepo من 10 حزم (Vite، TanStack Router/Query)، بمصادقة Cognito ‏(OAuth2 وSAML)
  • Terraform عبر 24 وحدة و10 بيئات، منشورة في منطقتين (طوكيو وأوساكا) لاستعادة الكوارث

المعمارية السداسية داخل Lambda

خمسون دالة تتشارك قاعدة كود واحدة تتعفن سريعًا بلا بنية. كل دالة اتبعت التخطيط السداسي نفسه:

src/
  core/domains/<domain>/   # domain، application (حالات الاستخدام)، ports
  adapters/
    primary/               # معالجات http، المخططات، المقدّمات
    secondary/             # الإتاحة (Prisma)، التخزين، الطوابير
  containers/              # مصانع حالات الاستخدام — التوصيل في مكان واحد

القاعدة التي رسّخت الأمر: المعالج عشرة أسطر. حلّل الحدث، استدعِ حالة الاستخدام، حوّل النتيجة. كل منطق العمل يعيش في حالات استخدام تتلقى منافذ (واجهات)، ما يعني أن اختبارات الوحدة تعمل على بدائل في الذاكرة دون أي محاكاة لـ AWS. بقيت حزم الاختبار المحلية في نطاق مئات المللي ثانية، ويمكن لحالة الاستخدام نفسها أن يتقدمها معالج API Gateway اليوم ومعالج SQS غدًا.

سدّد هذا ثمنه بأوضح صورة في خط الدفعات، حيث عملت حالة استخدام التحقق نفسها في ثلاثة سياقات: فحوص متزامنة لسجل واحد من الـ API، وتحقق جماعي غير متزامن من SQS، وجولة إعادة تحقق أثناء التسجيل.

الخط غير المتزامن: SQS عمودًا فقريًا

جرت معالجة الملفات كسلسلة مراحل، كل مرحلة طابور ومستهلك Lambda:

رفع ← تحليل/توحيد ← تحقق (مطابقة البيانات المرجعية) ← تسجيل (SFTP) ← إشعار

قرارات التصميم التي أحدثت فرقًا:

  • طابور لكل مرحلة، لا طابور واحد كبير. كل مرحلة تتوسع وتُخنق وتفشل باستقلال. التسجيل مقيّد بساعات عمل نظام خارجي؛ التحقق ليس كذلك. فصل الطوابير سمح للتسجيل بالتكدّس ليلًا دون تعطيل التحقق.
  • DLQ في كل مكان، وإعادة التغذية خطوة في الـ runbook. لكل مستهلك طابور رسائل ميتة وإنذار على عمقه. الرسالة السامة توقف السجل، لا الخط.

رسم متحرك: سجلات تتدفق عبر مراحل التحليل والتحقق والتسجيل؛ رسالة سامة تسقط من مرحلة التحقق إلى DLQ، ينطلق إنذار، والسجلات السليمة تواصل التدفق

  • التحقق مقابل بيانات مرجعية مخبّأة. فحوص الوجود (رموز الفروع، البيانات المرجعية للعناوين) تطرق الاستعلامات نفسها بلا توقف، فوضعنا طبقة cache أمام Postgres — بما فيها تخبئة النتائج السلبية: في حمل تحقق، «هذا الرمز غير موجود» يُستعلم عنه بقدر «موجود» ويستحق التخبئة بالقدر نفسه. هذا التغيير الواحد أزال فئة كاملة من قفزات حمل قاعدة البيانات أثناء الرفع الجماعي.
  • أحداث تقدم عبر WebSocket. عمل دفعات طويل مع واجهة صامتة يولّد تذاكر دعم. كل مرحلة تنشر تقدمها، وLambda صغيرة تبث عبر WebSocket API، والـ SPA تعرض الحالة حيّة. رخيص البناء، وعائده على تجربة المستخدم غير متناسب.

Aurora + Lambda: مشكلة الاتصالات

نموذج توسّع Lambda ونموذج اتصالات Postgres عدوّان بالفطرة — دفقة تنفيذ متزامن تستنزف الاتصالات فورًا. يجلس RDS Proxy بينهما ويجمّع الاتصالات عبر الاستدعاءات. مع Prisma كانت القواعد العملية: أنشئ العميل خارج المعالج مرة واحدة، وأبقِ حجم التجمع لكل نسخة في حده الأدنى، ودَع البروكسي يتولى التجميع. تحت دفقات الرفع الجماعي سطّح البروكسي قفزات اتصالات كانت ستُسقط قاعدة البيانات لولاه.

استعادة كوارث متعددة المناطق تتمرّن عليها فعلًا

نُشرت المنصة في طوكيو أساسًا وأوساكا احتياطًا: Terraform يطبّق المنطقتين من الوحدات نفسها، وAurora يستنسخ عبر المناطق، ودلاء S3 تستنسخ لاإتزامنيًا. درسان:

  1. استعادة الكوارث ميزة منتَج، لا فكرة لاحقة للبنية التحتية. ‏runbook التحويل — DNS، ترقية قاعدة البيانات، ترتيب تصريف الطوابير — كُتب وتُمُرِّن عليه؛ لأن DR غير المختبَر رسمٌ بياني لا قدرة.
  2. وحدات Terraform تثبت جدواها عند المنطقة الثانية. إقامة أوساكا كانت ملف متغيرات لا مشروعًا. لو بنينا طوكيو يدويًا لانحرفت منطقة الـ DR من اليوم الأول.

تخطيط الشبكة كان بالمثل بلا بريق لكنه حاملٌ للوزن: VPC من أربع طبقات (شبكات فرعية: جدار ناري / عامة / محمية / خاصة)، وNetwork Firewall لضبط الخارج، وحزمة التدقيق الكاملة — CloudTrail وGuardDuty وSecurity Hub وConfig — مع استعلامات Athena فوق VPC Flow Logs جوابًا على «أثبت ما الذي تحدث مع ماذا».

قابلية الرصد

تتبّع X-Ray الطلبات عبر API Gateway ← Lambda ← SQS ← Lambda ← Aurora، فحوّل «الرفع بطيء» من تنقيب أثري إلى رسم لهب. إنذارات CloudWatch راقبت الأشياء المملة التي تتنبأ فعلًا بالحوادث: عمق DLQ، عمر الطوابير، معدلات أخطاء Lambda وخنقها، تشبّع اتصالات RDS Proxy. ودليل استعلامات سجلات ثنائي اللغة وثّق وصفات الفريق في Logs Insights، لأن المعرفة القبلية بكيفية عدّ إخفاقات الأمس ليست استراتيجية مراقبة.

مقايضات صادقة

كان serverless الخيار الصحيح هنا — أحمال دفعات متقلبة، متطلبات أمن مؤسسية، فريق صغير — لكنه لم يكن مجانيًا:

  • البدء البارد كان محتملًا في الدفعات وملحوظًا في طبقة الـ API؛ الحل العملي provisioned concurrency لحفنة الدوال الحساسة للاستجابة.
  • التطوير المحلي يحتاج استثمارًا. أنقذت المعمارية السداسية اختبارات الوحدة، لكن التدفقات الشاملة ظلت تتطلب بيئة منشورة؛ عشر بيئات مُدارة بـ Terraform وُجدت تحديدًا ليكون لكل مهندس ومرحلة بيئة حقيقية.
  • خمسون دالة صغيرة أثقل تشغيليًا من خمس خدمات. التعويض أن لكل دالة مهمة واحدة وطابورًا واحدًا وإنذارًا واحدًا — حين ينكسر شيء، نصف قطر الانفجار يسمّي نفسه.

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


هذه المقالة ترسو عليها سلسلة قصيرة عن المنصة نفسها: المعمارية السداسية داخل Lambda، مع الكود، استعادة كوارث متعددة المناطق تتمرّن عليها فعلًا، صمّم سجلّاتك كأنها API.