20 مارس 2025·7 دقيقة قراءة

العمليات الموجهة بالأحداث مقابل واجهات الطلب-الرد للمهام الطويلة

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

العمليات الموجهة بالأحداث مقابل واجهات الطلب-الرد للمهام الطويلة

لماذا تعد العمليات الطويلة التوقيت صعبة في تطبيقات الأعمال

نُسَبّ العملية إلى "طويلة" عندما لا تنتهي في خطوة سريعة واحدة. قد تستغرق دقائق أو ساعات أو أيام لأنها تعتمد على أشخاص، وقت، أو أنظمة خارجية. أي شيء يتضمن موافقات، تسليمات، وانتظار يندرج هنا.

هنا يبدأ تفكير واجهات برمجة التطبيقات بطريقة الطلب-الرد في الانهيار. مكالمة API مصممة لتبادل قصير: أرسل طلبًا، استلم جوابًا، تابع. المهام الطويلة أشبه بقصة على فصول؛ تحتاج أن تتوقف، تتذكّر بالضبط أين توقفت، وتستأنف لاحقًا دون تخمين.

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

عندما يتعامل الفريق مع عملية طويلة كنداء API واحد، تظهر بعض المشاكل المتوقعة:

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

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

لهذا السبب يكون الاختيار بين العمليات الموجهة بالأحداث وواجهات الطلب-الرد مهمًا للعمليات الطويلة في الأعمال.

نموذجان ذهنيان: مكالمات متزامنة مقابل أحداث على مدى الزمن

أبسط مقارنة تختزل في سؤال واحد: هل ينتهي العمل بينما ينتظر المستخدم، أم يستمر بعد رحيله؟

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

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

الفرق العملي هو الحالة.

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

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

مثال بسيط: "إرسال تقرير مصاريف" يمكن أن يكون متزامنًا. "الحصول على موافقة، الانتظار 3 أيام، تذكير المدير، ثم الدفع" ليس كذلك.

الموافقات: كيف يتعامل كل نهج مع القرارات البشرية

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

مع واجهات الطلب-الرد، تتحول الموافقات غالبًا إلى شكل محرج:

  • الحجب (غير عملي)
  • الاستطلاع الدوري (العميل يسأل "هل تمت الموافقة؟" مرارًا)
  • ردود المكالمات / webhooks (الخادم يتصل بك لاحقًا)

كلها ممكنة، لكنها تضيف توصيلات لتجسير "زمن الإنسان" مع "زمن API".

مع الأحداث، تُقرأ الموافقة كقصة. يسجل التطبيق شيئًا مثل "ExpenseSubmitted"، ثم لاحقًا يصل "ExpenseApproved" أو "ExpenseRejected". محرك سير العمل (أو آلة الحالة الخاصة بك) ينقل السجل فقط عند وصول الحدث التالي. هذا يتماشى مع طريقة تفكير معظم الناس في خطوات العمل: أرسل، راجع، قرر.

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

نموذج موافقة بسيط وقابل للتوسع

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

التقط بعض القطع من البيانات كسجلات ذات أولوية:

  • طلب الموافقة نفسه: ما الذي يُوافق عليه والحالة الحالية
  • القرارات الفردية: من قرر، قبول/رفض، الطابع الزمني، السبب
  • المُلزمون بالموافقة: دور أو شخص، وأي قواعد ترتيب
  • قواعد النتيجة: "أي واحد"، "أغلبية"، "الكل مطلوب"، "يسمح بالتجاوز"

مهما كانت الطريقة التي تختارها، احفظ دائمًا من وافق ماذا ومتى ولماذا كبيانات، لا كسطر سجل بسيط.

المؤقتات والانتظار: التذكيرات، المواعيد النهائية، والتصعيد

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

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

تعامل سير العمل مع الوقت كخطوة عادية. يمكنك أن تقول: انتظر 24 ساعة، أرسل تذكيرًا، ثم انتظر حتى 48 ساعة إجمالًا وصعّد إلى مُوافق مختلف. النظام يحتفظ بالحالة، لذا لا تكون المواعيد النهائية مخفية في مشروع "cron + استعلامات" منفصل.

قد تقرأ قاعدة موافقة بسيطة هكذا:

بعد إرسال تقرير المصاريف، انتظر 1 يوم. إذا كانت الحالة لا تزال "قيد الانتظار"، أرسل رسالة إلى المدير. بعد يومين، إذا ظلت قيد الانتظار، أعد تعيينها إلى قائد المدير وسجل التصعيد.

التفصيل المهم هو ما تفعله عندما يعمل المؤقت لكن تغير العالم. أي سير عمل جيد يعيد التحقق من الحالة الحالية قبل التصرف:

  • حمّل أحدث حالة
  • تأكد أنها لا تزال قيد الانتظار
  • تأكد أن المُعيّن لا يزال صالحًا (قد تحدث تغييرات في الفريق)
  • سجل ما قررت ولماذا

إعادة المحاولات واسترداد الأخطاء دون تكرارات

Design events as a workflow
Turn your process into states and events using visual logic instead of fragile scripts.
Start Building

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

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

حل عملي هو مفتاح عدم التكرار (idempotency key): يرسل العميل رمزًا فريدًا مثل pay:invoice-583:attempt-1. يخزن الخادم النتيجة لذلك المفتاح ويعيد نفس النتيجة عند التكرارات. هذا يمنع الرسوم المزدوجة، التذاكر المكررة، أو الموافقات المكررة.

لعمليات الموجهة بالأحداث، خطر التكرار يختلف. غالبًا ما تُسلم الأحداث "على الأقل مرة"، مما يعني أن التكرارات قد تظهر حتى عندما يعمل كل شيء بشكل صحيح. يحتاج المستهلكون لإزالة التكرارات: سجّل معرف الحدث (أو مفتاحًا تجاريًا مثل invoice_id + step) وتجاهل التكرارات. هذا فرق أساسي في أنماط تنسيق سير العمل: يركز الطلب-الرد على إعادة تشغيل آمنة للنداءات، بينما تركز الأحداث على إعادة تشغيل آمنة للرسائل.

بعض قواعد إعادة المحاولة المفيدة في أي نموذج:

  • استخدم تراجعًا زمنيًا متزايدًا (مثلاً 10s, 30s, 2m).
  • ضع حدًا أقصى لعدد المحاولات.
  • فرّق بين الأخطاء المؤقتة (إعادة المحاولة) والأخطاء الدائمة (الفشل السريع).
  • وجه الإخفاقات المتكررة إلى حالة "تحتاج انتباهًا".
  • سجّل كل محاولة حتى تستطيح شرح ما حدث لاحقًا.

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

سجلات التدقيق: جعل العملية قابلة للتفسير

Test your approval scenario
Prototype an expense approval with reminders, escalation, and payment retry logic.
Prototype Now

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

لأي عملية طويلة، سجّل الحقائق التي تتيح لك إعادة تشغيل القصة:

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

تُسهّل العمليات الموجهة بالأحداث التدقيق لأن كل خطوة تنتج حدثًا طبيعيًا مثل "ManagerApproved" أو "PaymentFailed". إذا خزنت تلك الأحداث مع الحمولة والفاعل، تحصل على جدول زمني نظيف. المفتاح هو أن تبقي الأحداث وصفية وتخزنها في مكان يمكن الاستعلام عنه حسب القضية.

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

إصلاح بسيط هو معرف قضية مشترك (correlation ID). إنه معرف واحد تلحقه بكل طلب، حدث، وسجل قاعدة بيانات متعلق بعملية معينة، مثل "EXP-2026-00173". عندها يمكنك تتبع الرحلة الكاملة عبر الخطوات.

اختيار النهج المناسب: نقاط القوة والمقايضات

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

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

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

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

ثمة مقايضات حقيقية:

  • البساطة مقابل الديمومة: الطلب-الرد أبسط للبدء، الأحداث أكثر أمانًا للتأخيرات الطويلة.
  • أسلوب التصحيح: الطلب-الرد يتبع خطًا مستقيمًا، بينما تتطلب تدقيقات سير العمل تتبعًا عبر خطوات.
  • الأدوات والعادات: الأحداث تحتاج تسجيلًا جيدًا، معرفات الارتباط، ونماذج حالة واضحة.
  • إدارة التغيير: تتفرع سير العمل وتتطور؛ تصاميم الأحداث تتعامل مع المسارات الجديدة أفضل عندما تُصوَّر جيدًا.

مثال عملي: تقرير مصاريف يحتاج موافقة المدير ثم مراجعة المالية ثم الدفع. إذا فشل الدفع، تريد محاولات آمنة دون دفع مزدوج. هذا طبيعيًا موجه بالأحداث. إذا كان الهدف مجرد "إرسال مصروف" مع فحوصات سريعة، فالطلب-الرد غالبًا يكفي.

خطوة بخطوة: تصميم عملية طويلة التوقيت تصمد أمام التأخيرات

Store process state properly
Set up PostgreSQL data models for requests, decisions, deadlines, and correlation IDs.
Model Data

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

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

تسلسل تصميم بسيط

  1. ضع حدودًا: عرّف مُحفّز البدء، شرط النهاية، وبعض الحالات الرئيسية (قيد الموافقة، مقبول، مرفوض، منتهي الصلاحية، مكتمل).
  2. سمّ الأحداث والقرارات: اكتب ما الذي يمكن أن يحدث عبر الزمن (Submitted, Approved, Rejected, TimerFired, RetryScheduled). حافظ على أسماء الأحداث بصيغة الماضي.
  3. اختر نقاط الانتظار: حدّد أين تتوقف العملية لشخص، نظام خارجي، أو موعد نهائي.
  4. أضف قواعد مؤقت وإعادة المحاولة لكل خطوة: قرر ما الذي يحدث عند مرور الوقت أو فشل نداء (تراجع زمني، حد أقصى للمحاولات، تصعيد، الاستسلام).
  5. عرّف كيف تستأنف العملية: عند كل حدث أو رد نداء، حمّل الحالة المحفوظة، تحقق أنها ما تزال صالحة، ثم انتقل إلى الحالة التالية.

للصمود أمام إعادة التشغيل، احفظ الحد الأدنى من البيانات اللازمة للمتابعة بأمان. خزن ما يكفي لإعادة التشغيل بدون تخمين:

  • معرف مثيل العملية والحالة الحالية
  • من يمكنه التصرف تالياً (مُعيّن/دور) وماذا قرروا
  • المواعيد النهائية (due_at, remind_at) ومستوى التصعيد
  • بيانات إعادة المحاولة (عدد المحاولات، آخر خطأ، next_retry_at)
  • مفتاح عدم التكرار أو أعلام "تم التنفيذ" للتأثيرات الجانبية (إرسال رسالة، خصم بطاقة)

إذا استطعت إعادة بناء "أين نحن" و"ما المسموح به تالياً" من البيانات المحفوظة، تتوقف المخاوف من التأخيرات.

أخطاء شائعة وكيف تتجنبها

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

الأخطاء الشائعة:

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

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

تأتي معظم الإصلاحات من كونك صريحًا:

  • خزن تحولات الحالة (Requested, Approved, Rejected, Paid) في قاعدة بيانات مع من/ما غيرها.
  • استخدم مفاتيح عدم التكرار لكل أثر جانبي خارجي (مدفوعات، رسائل، تذاكر) وخزن النتيجة.
  • فصل "قبول الطلب" عن "إتمام العمل": عد بسرعة، ثم أتمم سير العمل في الخلفية.
  • نمطن الطوابع الزمنية (UTC)، أضف معرفات الارتباط، وسجل كل من الطلب والنتيجة.

قائمة تحقق سريعة قبل البناء

Keep a clean code path
Generate real source code when you need full control over hosting or review.
Export Code

العمل الطويل أقل عن نداء مثالي واحد وأكثر عن البقاء صحيحًا بعد التأخيرات، الأشخاص، والأخطاء.

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

قائمة تحقق عملية:

  • عرّف كيف تستأنف العملية بعد تعطل أو نشر. ما الحالة المحفوظة، وما الذي يعمل تالياً؟
  • أعطِ كل مثيل مفتاح عملية فريدًا (مثل ExpenseRequest-10482) ونموذج حالة واضح (Submitted, Waiting for Manager, Approved, Paid, Failed).
  • عامل الموافقات كسجلات، لا نتائج فقط: من وافق أو رفض، متى، والسبب أو تعليق.
  • خرّط قواعد الانتظار: تذكيرات، مواعيد نهائية، تصعيدات، انتهاء صلاحية. سمِّ مالكًا لكل مؤقت (مدير، مالية، نظام).
  • خطط للتعامل مع الفشل: يجب أن تكون المحاولات محدودة وآمنة، ويجب أن يكون هناك توقف "يحتاج مراجعة" حيث يستطيع شخص إصلاح البيانات أو الموافقة على إعادة محاولة.

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

مثال: موافقة مصاريف مع مهلة وإعادة محاولة دفع

Build durable approval workflows
Model a long-running approval flow with stored state, retries, and clear audit fields.
Try AppMaster

السيناريو: موظف يقدّم إيصال تاكسي بقيمة $120 لاسترداد. يحتاج موافقة المدير خلال 48 ساعة. إذا تم الموافقة، يدفع النظام للموظف. إذا فشل الدفع، يعيد المحاولة بأمان ويترك سجلًا واضحًا.

لمحة عبر request-response

مع واجهات الطلب-الرد، يتصرف التطبيق غالبًا كمحادثة يجب أن تستمر في الفحص.

ينقر الموظف إرسال. ينشئ الخادم سجل استرداد الحالة "قيد الانتظار" ويعيد معرّفًا. يتلقى المدير إشعارًا، لكن غالبًا يجب على تطبيق الموظف الاستطلاع لمعرفة التغييرات: "GET reimbursement status by ID".

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

عندما يوافق المدير، يغيّر الخادم الحالة إلى "مقبول" وينادي مزود الدفع. إذا أعطت Stripe خطأً مؤقتًا، يجب على الخادم أن يقرر إعادة المحاولة الآن، لاحقًا، أم الفشل. بدون مفاتيح عدم التكرار بعناية، قد تخلق إعادة المحاولة دفعة مزدوجة.

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

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

الموظف يقدّم، فينتج حدث "ExpenseSubmitted". يبدأ سير عمل وينتظر إما "ManagerApproved" أو حدث مؤقت "DeadlineReached" بعد 48 ساعة. إذا نفد المؤقت أولاً، يسجل سير العمل نتيجة "AutoRejected" والسبب.

عند الموافقة، يسجل سير العمل "PayoutRequested" ويحاول الدفع. إذا توقفت Stripe مؤقتًا، يسجل "PayoutFailed" مع رمز الخطأ، يحدد إعادة محاولة (مثلاً بعد 15 دقيقة)، ولا يسجل "PayoutSucceeded" إلا مرة واحدة باستخدام مفتاح عدم التكرار.

ما يراه المستخدم يبقى بسيطًا:

  • قيد الانتظار (48 ساعة متبقية)
  • مقبول، جاري الدفع
  • إعادة محاولة الدفع مجدولة
  • مدفوع

يقرأ سجل التدقيق كسطر زمني: قدم، قُبل، فحص المهلة، حاول الدفع، فشل، أعيدت المحاولة، دُفِع.

الخطوات التالية: تحويل النموذج إلى تطبيق عمل

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

اكتب العملية كحالات وأحداث، لا كشاشات. على سبيل المثال: "Submitted" -> "ManagerApproved" -> "PaymentRequested" -> "Paid"، مع تفرعات مثل "ApprovalRejected" أو "PaymentFailed". عندما ترى نقاط الانتظار والتأثيرات الجانبية بوضوح، يصبح الاختيار بين العمليات الموجهة بالأحداث وواجهات الطلب-الرد عمليًا.

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

أضف حقول التدقيق منذ اليوم الأول. خزّن من فعل ماذا، متى حدث، ولماذا (تعليق أو رمز سبب). عندما يسأل أحدهم "لماذا أُعيدت محاولة الدفع؟" تريد إجابة واضحة دون حفر في السجلات.

إذا كنت تبني هذا النوع من سير العمل على منصة دون كود، AppMaster (appmaster.io) هو خيار حيث يمكنك نمذجة البيانات في PostgreSQL وبناء منطق العملية بصريًا، مما يجعل الموافقات وسجلات التدقيق أسهل في الاتساق عبر تطبيقات الويب والهاتف.

الأسئلة الشائعة

When should I use request-response instead of an event-driven workflow?

استخدم request-response عندما تنتهي المهمة بسرعة وبشكل متوقع بينما ينتظر المستخدم — مثل إنشاء سجل أو التحقق من نموذج. استخدم نموذج العمل الموجّه بالأحداث عندما تمتد العملية لعدة دقائق أو أيام، تتضمن موافقات بشرية، أو تحتاج مؤقتات، ومحاولات إعادة، واستئناف آمن بعد إعادة تشغيل الخدمة.

Why do long-running processes break when built like a single API call?

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

How do I make a long-running process resumable after a restart?

الافتراضي الجيد هو حفظ حالة عملية واضحة في قاعدة البيانات والمضي قدماً فقط عبر تحويلات صريحة. خزن معرف النسخة (process instance ID)، الحالة الحالية، من يمكنه التصرف تالياً، والطوابع الزمنية الرئيسة حتى تستطيع الاستئناف بأمان بعد نشر جديد أو تعطل.

What’s the cleanest way to handle human approvals?

نمذِج الموافقات كخطوة متوقفة تستأنف عندما يصل قرار، بدلاً من حجب الطلب أو الاستطلاع المستمر. سجِّل كل قرار كبيانات (من قرر، متى، قبول/رفض، والسبب) حتى يمكن للمسار الانتقال بشكل متوقع ولأغراض التدقيق لاحقاً.

Is polling for approval status a bad idea?

يمكن أن يعمل الاستطلاع (polling) للحالات البسيطة، لكنه يضيف ضوضاء وتأخير لأن العميل يستمر في السؤال «هل انتهى؟». الافضل دفع إشعار عند التغيير ودع العميل يحدث العرض عند الحاجة، بينما يكون الخادم هو مصدر الحقيقة للحالة.

How should I implement reminders, deadlines, and escalations?

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

How do I prevent double payments or duplicate emails when retries happen?

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

How do I handle duplicate events in an event-driven system?

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

What should an audit trail include for long-running workflows?

التقط تسلسلًا من الحقائق: الفاعل، الطابع الزمني، المدخلات في تلك اللحظة، النتيجة، وإصدار القاعدة/السياسة المستخدمة. أضف أيضاً معرف قضية واحد (correlation ID) لكل ما يتعلق بالعملية حتى يتمكن الدعم من الإجابة «أين عالقة؟» بدون البحث في سجلات غير ذات صلة.

What’s a practical data model for approvals that won’t get messy as rules grow?

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

من السهل أن تبدأ
أنشئ شيئًا رائعًا

تجربة مع AppMaster مع خطة مجانية.
عندما تكون جاهزًا ، يمكنك اختيار الاشتراك المناسب.

البدء