هندسة البرمجياتJune 13, 2026·12 دقيقة قراءة·Miracle KaluMiracle Kalu

هندسة الحدث الموجهة في الإنتاج: دروس من خط أنابيب طلبات عالي الإنتاجية

Abstract visualization of flowing data through a distributed event-driven pipeline

وعد هندسة الحدث الموجهة

هندسة الحدث الموجهة سهلة البيع وصعبة التشغيل. العرض مغرٍ: فك ارتباط خدماتك، وتوسيع النطاق بشكل مستقل، والاستجابة للتغييرات في الوقت الفعلي، وبناء أنظمة تبدو حية. الواقع هو كومة من المفاضلات حول الترتيب، وإزالة التكرار، والملاحظة، وتطور المخطط، وأنماط الفشل التي تتجاهلها معظم الدروس التعليمية.

قبل عامين، أعاد فريقي كتابة معالج طلبات أحادي ضخم في خط أنابيب موجه بالأحداث. يعالج النظام اليوم عدة ملايين من الأحداث يوميًا عبر الدفع والدفع والمخزون والتنفيذ والإشعارات. استندت الهجرة بشكل كبير إلى الأنماط المؤسسية في تصنيف مارتن فولر لهندسة الحدث الموجهة، وكذلك على الدروس التشغيلية من شركات مثل LinkedIn وUber وNetflix وSlack التي تدير منصات موجهة بالأحداث على نطاق الإنترنت. هذا المقال هو الدليل الذي أتمنى لو كان لدينا قبل أن نبدأ.

لماذا اخترنا الأحداث

كان النظام القديم سلسلة متزامنة من استدعاءات API. عندما يقدم العميل طلبًا، يستدعي معالج الويب خدمة المخزون، التي تستدعي خدمة الدفع، التي تستدعي خدمة التنفيذ، التي تستدعي خدمة الإشعارات. كل قفزة تضيف زمن استجابة وتضاعف نطاق تأثير الفشل.

إذا كانت خدمة الإشعارات بطيئة، ينتهي وقت الدفع. إذا كانت خدمة المخزون تواجه ارتفاعًا مؤقتًا، تفشل المدفوعات. إذا كانت خدمة التنفيذ متوقفة، يتم رفض الطلب بأكمله على الرغم من أن الدفع قد تم تحصيله بالفعل. كان النظام مترابطًا بإحكام بطريقة جعلت الحوادث التشغيلية متوقعة والتعافي بطيئًا.

حلت الأحداث مشكلة الارتباط المباشر. ينشر معالج الويب حدث OrderPlaced ويعود فورًا. تستهلك خدمات المخزون والدفع والتنفيذ والإشعارات الحدث بشكل مستقل. إذا كانت الإشعارات بطيئة، فلا يهتم الدفع. إذا كانت خدمة التنفيذ متوقفة مؤقتًا، فلا تزال الطلبات مقبولة ويتم معالجتها لاحقًا.

كانت هذه هي النظرية. تطلبت الممارسة الإجابة على قائمة طويلة من الأسئلة لم نكن قد فكرنا فيها بالكامل.

الأنماط الأربعة لهندسة الحدث الموجهة

يحدد مارتن فولر أربعة أنماط متميزة غالبًا ما يجمعها الناس تحت تصنيف "event-driven". فهم الاختلافات أمر أساسي لأن كل نمط يحل مشكلة مختلفة ويقدم بنية تكلفة مختلفة.

إشعار الحدث هو أخف الأنماط. تنشر الخدمة رسالة صغيرة تقول إن شيئًا ما قد حدث، وتستجيب الخدمات المتدنية. يحتوي الحدث عادةً على معرف فقط. المستهلكون الذين يحتاجون إلى مزيد من المعلومات يجب أن يستعلموا من المنتج. هذا يبقي الحمولات صغيرة لكنه يحافظ على بعض الارتباط لأن المستهلكين لا يزالون بحاجة إلى معرفة كيفية التواصل مع المنتج.

نقل الحالة عبر الحدث يتضمن بيانات كافية في الحدث حتى يتمكن المستهلكون من التصرف دون إجراء مكالمة راجعة. يحمل حدث CustomerAddressChanged العنوان الجديد. يحمل حدث OrderPlaced عناصر الطلب. هذا يقلل الارتباط ويحسن المرونة لأن المستهلكين يمكنهم الاستمرار في العمل حتى عندما يكون المنتج غير متاح. التكلفة هي حمولات أكبر، وحالة مكررة، وتناسق نهائي.

مصادر الأحداث تجعل سجل الأحداث مصدر الحقيقة. بدلًا من تخزين الحالة الحالية في قاعدة بيانات ونشر الأحداث كآثار جانبية، تقوم بإلحاق الأحداث بسجل غير قابل للتغيير واشتقاق الحالة الحالية عن طريق إعادة تشغيلها. Git هو المثال الكلاسيكي: شجرة العمل مشتقة من سجل الالتزام. توفر مصادر الأحداث مسارات تدقيق قوية واستعلامات زمنية وقدرة على إعادة بناء الحالة، لكنها تضيف تعقيدًا كبيرًا حول تطور المخطط، وتكامل الأنظمة الخارجية، واللقطات.

CQRS يفصل النموذج المستخدم للكتابة عن النموذج المستخدم للقراءة. إنه لا يتعلق بشكل صارم بالأحداث، لكنه يتناسب بشكل طبيعي مع مصادر الأحداث ونقل الحالة عبر الحدث لأن الأحداث يمكنها تغذية.projections المحسّنة للقراءة. نموذج الكتابة يتعامل مع الأوامر وينبثق أحداثًا؛ نموذج القراءة يستهلك الأحداث ويحافظ على projections مخصصة لاستعلامات معينة.

يستخدم خط الأنابيب لدينا نقل الحالة عبر الحدث لدورة حياة الطلب الأساسية، وإشعار الحدث للمحفزات الخفيفة، وشكلًا محدودًا من مصادر الأحداث لسجل التدقيق. تجنبنا CQRS الكامل لمعظم الخدمات لأن نماذج القراءة والكتابة لم تكن مختلفة بما يكفي لتبرير الانقسام.

تصميم الأحداث هو أهم قرار

أكبر خطأ ترتكبه الفرق هو التعامل مع الأحداث كأغلفة رقيقة حول تغييرات قاعدة البيانات. ينشرون أحداث OrderCreated وOrderUpdated وOrderDeleted التي تعكس جدول CRUD. هذا يُسرب الحالة الداخلية ويجبر المستهلكين على إعادة بناء النية من تدفق من الاختلافات.

تعلمنا تصميم الأحداث حول نية العمل، وليس صفوف قاعدة البيانات. بدلًا من OrderUpdated، نستخدم OrderPlaced وPaymentConfirmed وInventoryReserved وOrderShipped وOrderCancelled. يصف كل اسم حدث شيئًا حدث في العمل، وليس شيئًا تغير في جدول.

الحدث المصمم جيدًا يجيب على ثلاثة أسئلة:

  1. ما الذي حدث؟ استخدم فعلًا في الماضي في اسم الحدث.
  2. لمن؟ أضف معرف التجميع وسياقًا كافيًا لفهم الموضوع.
  3. لماذا؟ أضف البيانات التي يحتاجها المستهلك للتصرف، دون تضمين كل ما قد تحتاجه يومًا ما.
{
  "eventId": "evt_01J8XQ...",
  "eventType": "OrderPlaced",
  "aggregateId": "order_48291",
  "timestamp": "2026-06-10T14:23:11Z",
  "payload": {
    "orderId": "order_48291",
    "customerId": "cust_7721",
    "currency": "EUR",
    "total": 149.95,
    "lineItems": [
      { "sku": "SHOE-42-BLK", "quantity": 1, "unitPrice": 89.95 },
      { "sku": "SOCK-3PK", "quantity": 2, "unitPrice": 30.00 }
    ],
    "shippingAddress": {
      "country": "DE",
      "postalCode": "10115"
    }
  }
}

لاحظ ما ليس في الحدث. لا يوجد مفتاح أساسي داخلي لقاعدة البيانات، ولا حقل إصدار، ولا تعداد حالة، ولا بيانات يحتاجها مستهلك واحد فقط. الحدث هو عقد، وكل حقل فيه التزام.

الرقص مقابل التنظيم

بمجرد أن يكون لديك أحداث، يجب أن تقرر من ينسق سير العمل. هناك نهجان واسعان.

الرقص يعني أن كل خدمة تستجيب للأحداث بشكل مستقل وتنبثق أحداث جديدة عندما تنتهي. لا يوجد منسق مركزي. خدمة الطلبات تنبثق OrderPlaced؛ المخزون يستهلكها وينبثق InventoryReserved؛ الدفع يستهلك OrderPlaced وينبثق PaymentConfirmed؛ التنفيذ ينتظر كل من InventoryReserved وPaymentConfirmed قبل الشحن. هذا مترابط بإحكام ويوسع النطاق بشكل جيد، لكن سير العمل العالمي ضمني. لا يمكنك قراءة جزء واحد من الكود ورؤية العملية بأكملها.

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

نستخدم الرقص لتدفق الطلبات القياسي لأن الخطوات مفهومة جيدًا والخدمات مستقرة. نستخدم التنظيم لسير عمل الاسترداد والاستبدال المعقدة لأنها تتضمن منطق التفرع، والإجراءات التعويضية، ومهلات الانتظار التي يصعب التعبير عنها كرقص خالص. تستخدم العديد من أنظمة الإنتاج، بما في ذلك أنظمة Uber وNetflix، كليهما في النهاية.

الترتيب والتقسيم ليسا اختياريين

كانت إحدى المفاجآت الأولى أن مستهلكي الأحداث لا يعالجون الأحداث تلقائيًا بالترتيب الذي يتوقعه الإنسان. إذا قدم العميل طلبًا ثم ألغاه على الفور، فقد يصل حدث OrderCancelled قبل حدث OrderPlaced في قسم أو مجموعة مستهلكين مختلفة.

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

كانت حلولنا هي تقسيم الأحداث حسب معرف التجميع. يذهب كل حدث متعلق بنفس الطلب إلى نفس القسم، مما يضمن الترتيب داخل تدفق هذا الطلب. المستهلكون الذين يحتاجون إلى ترتيب صارم يشتركون في تلك الأقسام. المستهلكون الذين لا يحتاجون إليه يمكنهم معالجة الأحداث من أي قسم بشكل متوازٍ.

function partitionKeyFor(event: OrderEvent): string {
  if (event.aggregateId.startsWith('order_')) {
    return event.aggregateId;
  }
  return event.eventType;
}

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

إعادة توازن مجموعات المستهلكين هي فخ آخر للترتيب. عندما ينضم مستهلك أو يغادر المجموعة، يتم إعادة تعيين الأقسام. إذا كان المستهلك قد عالج بعض الأحداث من قسم ولكنه لم يلتزم بالإزاحة بعد، فقد يعيد المستهلك المعين حديثًا معالجة تلك الأحداث. لهذا السبب، تعتبر إزالة التكرار ودلالات التسليم مرة واحدة على الأقل أمرًا لا جدال فيه.

إزالة التكرار تحفظ عقلك

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

يجب أن يكون كل مستهلك قادرًا على إزالة التكرار حسب التصميم. النمط الأبسط هو تتبع معرفات الأحداث المعالجة في مخزن إزالة تكرار بمدة صلاحية تطابق نافذة الاستبقاء الخاصة بك. النمط الأكثر قوة هو جعل العملية المتدنية نفسها قابلة لإزالة التكرار.

على سبيل المثال، يقبل معالج الدفع لدينا مفتاح idempotencyKey على كل طلب شحن. نستخدم معرف الحدث كمفتاح. إذا تمت معالجة حدث PaymentConfirmed نفسه مرتين، فإن المكالمة الثانية تُرجع الشحنة التي تم إنشاؤها سابقًا بدلاً من إنشاء شحنة جديدة.

async function handlePaymentConfirmed(event: PaymentConfirmedEvent): Promise<void> {
  const charge = await payments.charge({
    amount: event.payload.amount,
    currency: event.payload.currency,
    source: event.payload.paymentMethod,
    idempotencyKey: event.eventId,
  });

  await orderProjection.update(event.aggregateId, {
    paymentStatus: 'confirmed',
    chargeId: charge.id,
    paidAt: charge.createdAt,
  });
}

مفتاح إزالة التكرار ليس راحة. إنه العقد الذي يجعل مستهلك الحدث آمنًا عند إعادة المحاولة. بدونه، فإنك تبني نظامًا يعمل معظم الوقت ويفشل بشكل كارثي تحت الحمل.

دلالات Exactly-Once: تستحق العناء أم مبالغ فيها؟

اتجاه متزايد في الأنظمة الموجهة بالأحداث هو الدفع نحو دلالات المعالجة مرة واحدة بالضبط. يدعم Kafka منتجين غير قابلين للتكرار والمعاملات. يوفر Flink وKafka Streams ضمانات exactly-once لمعالجة التدفقات. بالنسبة للأنظمة المالية والصحية وتجارة التجزئة، يمكن أن تلغي exactly-once الحاجة إلى التسوية اليدوية.

لقد أخذنا exactly-once بعين الاعتبار لمعالجة الدفع، لكننا في النهاية بقينا مع at-least-once بالإضافة إلى مستهلكين غير قابلين للتكرار. السبب كان البساطة التشغيلية. تتطلب دلالات exactly-once منتجين معامليين، وتكوين مستهلك دقيق، وفهمًا عميقًا لكيفية تفاعل الالتزامات مع الآثار الجانبية. في نطاقنا وحجم فريقنا، كان المستهلكون غير القابلون للتكرار بالإضافة إلى المقاييس الواضحة أسهل في الفهم وتصحيح الأخطاء.

تعتمد القرار على مدى تحملك للتكرار ونضج فريقك التشغيلي. إذا كان الحدث المكرر سيؤدي إلى مشكلة تنظيمية أو مالية لا يمكن إصلاحها من خلال إزالة التكرار، فإن exactly-once تستحق التعقيد. خلاف ذلك، فإن at-least-once مع عمليات غير قابلة للتكرار عادةً ما تكون الخيار العملي.

معالجة الأخطاء وقوائم الرسائل الميتة

في النظام المتزامن، يُرجع الاستدعاء الفاشل عادةً خطأً إلى المتصل. في النظام الموجه بالأحداث، ليس للمستهلك الفاشل متصل. يبقى الحدث هناك، ويعيد المحاولة، ويحجب القسم، وربما يسمم المستهلكين المتدنيين.

نفذنا استراتيجية متدرجة لمعالجة الأخطاء:

  1. الإخفاقات العابرة تُعاد المحاولة باستخدام backoff أسي وتشويش. تندرج في هذا الصندوق مهلة الشبكة وتنافس قاعدة البيانات وحدود معدل الأطراف الثالثة.
  2. انتهاكات قواعد العمل توضع في قائمة حجر صحي للمراجعة اليدوية. هذه ليست أخطاءً؛ بل هي حالات لا يعرف الكود صراحةً كيفية التعامل معها.
  3. الإخفاقات الدائمة تنتقل إلى قائمة رسائل ميتة بعد عدد صغير من إعادات المحاولة. يُطلق تنبيه ويقوم مهندس بالتحقيق.

يعتمد عدد إعادات المحاولة واستراتيجية الانتظار على المستهلك. تُعاد محاولة معالجة الدفع ثلاث مرات خلال ثلاثين ثانية لأن شبكة البطاقات قد تكون غير متاحة لفترة وجيزة. تُعاد محاولة حجز المخزون بشكل أكثر عدوانية لأن نافذة البيع قصيرة. تُعاد محاولة إرسال الإشعارات لساعات لأن انقطاعًا مؤقتًا لمقدم الخدمة لا ينبغي أن يفقد الحدث.

من الأهمية بمكان أن لا تكسر إعادات المحاولة الترتيب. إذا فشل المستهلك في الحدث N واستمر في إعادة المحاولة، فإن الحدث N+1 في نفس القسم محظور. نستخدم موضوع إعادة محاولة مع آلية تأخير بحيث يتم إعادة إدراج الأحداث الفاشلة بينما يستمر القسم.

الضغط العكسي والتحكم في التدفق

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

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

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

الدفاع الثالث هو قاطع الدائرة. إذا كانت التبعية المتدنية مثل معالج الدفع متوقفة، فإننا نتوقف عن ضربها ونترك موضوع إعادة المحاولة يخزن الأحداث مؤقتًا. هذا يحمي التبعية ويمنع المستهلك من حرق وحدة المعالجة المركزية على طلبات مضمونة الفشل.

الملاحظة تغير كل شيء

تصحيح الأخطاء في الأنظمة الموجهة بالأحداث أصعب منه في الأنظمة المتزامنة. لا يمكنك متابعة طلب واحد عبر stack استدعاءات. يجب عليك إعادة بناء القصة من سجلات ومقاييس وتتبعات متناثرة.

بنينا ثلاث طبقات للملاحظة:

  • التتبع الموزع بمعرف ارتباط ينتشر من طلب HTTP الأولي عبر كل حدث يتم إنتاجه واستهلاكه. يعرض التتبع الواحد دورة حياة الطلب بالكامل.
  • مقاييس الأحداث بما في ذلك معدل النشر، ومعدل الاستهلاك، والتأخر لكل قسم، ومعدل إعادة المحاولة، ومعدل الرسائل الميتة. هذه هي العلامات الحيوية لخط الأنابيب.
  • سجل التدقيق لكل حدث تم نشره واستهلاكه، مخزن في مخزن كائنات مع نافذة استبقاء طويلة. هذا لا يقدر بثمن لتحقيقات الحوادث والامتثال.

المقياس الذي أنقذنا أكثر من غيره كان تأخر المستهلك. يخبرك الارتفاع المفاجئ في التأخر بأن المستهلك يتأخر قبل أن ينهار تمامًا. نُطلق التنبيهات على عتبات النسب المئوية للتأخر، وليس فقط على الأخطاء.

يبدأ كتاب تشغيل الحوادث القياسي لدينا بثلاثة أسئلة: هل ينشر المنتج؟ هل يلحق المستهلك بالركب؟ هل تنمو قائمة الرسائل الميتة؟ عادةً ما تخبرنا هذه المقاييس الثلاثة بأي جزء من خط الأنابيب مريض.

التسلسل وتطور المخطط

تعيش الأحداث أطول من الخدمات. بعد عام من نشر حدث ما، قد يستهلكه ثلاثة فرق جديدة ويغير منتجان أصليان شكله. إذا تعاملت مع مخططات الأحداث كأمر ثانوي، فإنك تنشئ حقل ألغام من الاعتماديات الضمنية.

بدأنا بـ JSON للقراءة والتصحيح، ثم انتقلنا إلى Avro للمواضيع الحرجة من حيث الأداء. وجدت دراسة لـ Confluent أن التبديل من JSON إلى Avro يمكن أن يقلل حجم الحمولة بحوالي سبعين بالمائة ويقلل زمن الوصول بعشرات المللي ثانية لكل حدث في البيئات عالية التكرار. بالنسبة لخط أنابيب يعالج ملايين الأحداث يوميًا، يحدث هذا الفرق.

نستخدم سجل مخططات مع فحوصات التوافق للأمام وللخلف. كل حدث له مخطط مع إصدار. يتحقق المنتجون من الأحداث مقابل المخطط قبل النشر. يعلن المستهلكون عن إصدارات المخططات التي يدعمونها.

القاعدة التي نفرضها بسيطة: تغييرات إضافية فقط، ما لم يتم ترحيل كل مستهلك. يمكنك إضافة حقول اختيارية. لا يمكنك إزالة حقول أو تغيير نوع حقول موجودة بدون دورة إهمال رسمية.

# schemas/order-placed/v1.yaml
name: OrderPlaced
version: 1
type: record
fields:
  - name: orderId
    type: string
  - name: customerId
    type: string
  - name: total
    type: decimal
    scale: 2
  - name: lineItems
    type: array
    items:
      type: record
      fields:
        - name: sku
          type: string
        - name: quantity
          type: int
        - name: unitPrice
          type: decimal
          scale: 2

عندما احتجنا إلى إضافة حقل giftMessage، أنشأنا v2 بالحقل الاختياري الجديد، وحدثنا المنتجين لنشر v2، وأعطينا المستهلكين مهلة لترحيل. نظرًا لأن الحقل كان اختياريًا، يمكن للمستهلكين الأقدم قراءة أحداث v2 دون تغييرات. تبدو هذه الانضباط بيروقراطيًا حتى يأتي تغييرك الكاسر الأول. بعد ذلك، تبدو وكأنها السبب الوحيد لاستمرار عمل النظام.

متى تكون هندسة الحدث الموجهة الخيار الخاطئ

هندسة الحدث الموجهة ليست ترقية عالمية. تعلمنا تجنبها في ثلاث حالات.

أولاً، عندما يكون التناسق القوي مطلوبًا ولا يمكن للأعمال تحمل التناسق النهائي. إذا كان يجب أن تعكس الإجراء فورًا في كل نموذج قراءة، فإن التنسيق المتزامن غالبًا ما يكون أبسط وأكثر أمانًا.

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

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

ما كنا سنفعله بشكل مختلف

إذا بدأنا من جديد، فسنصمم قائمة الأحداث قبل كتابة أي مستهلك. كتبنا الكود أولاً وسمينا الأحداث لاحقًا، مما أدى إلى مفردات غير متسقة عبر الخدمات. إعادة تسمية الأحداث مؤلمة لأن المستهلكين يعتمدون على الأسماء.

كنا سنستثمر أيضًا مبكرًا في تنبيهات تأخر المستهلك. في المرة الأولى التي تأخر فيها مستهلك أثناء بيع، اكتشفنا المشكلة من شكاوى العملاء، وليس من المقاييس. يعد تنبيه التأخر الآن أحد أهم إشاراتنا التشغيلية.

أخيرًا، كنا سنستخدم سجل مخططات من اليوم الأول. كان تكييف التحقق من المخطط على تدفق أحداث حي يعني تنسيق عمليات النشر عبر الفرق وقبول مخاطر مؤقتة كان يمكن تجنبها. كنا سنختار أيضًا Avro للمواضيع عالية الإنتاجية في وقت سابق، بدلًا من حمل overhead JSON حتى أصبح التأخر مرئيًا.

النقاط الرئيسية

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

الهدف ليس استخدام المزيد من الأحداث. الهدف هو بناء أنظمة تفشل بشكل أقل، وتتعافى بشكل أسرع، وتظل مفهومة مع نموها.

مشاركة:

XLinkedIn
Miracle Kalu

كتبه

Miracle Kalu

Senior Full Stack Engineer

أعجبك ما قرأته؟

أنا متاح لأدوار الهندسة الأولى والاستشارات التقنية. لنتحدث.

تواصل →

نُشر 13 يونيو 2026 · 12 دقيقة قراءة