ساخت فرایند سفارش با موتور Workflow لاراول
خلاصهٔ کاملتر
نویسنده میگه تقریباً هر اپلیکیشنی بالاخره یه «فرایند» پیدا میکنه، نه یه فیچر: چیزی که شروع میشه، پول میگیره، منتظر یه سرویس بیرونی میمونه، گاهی یه آدم باید نگاهش کنه و ساعتها یا روزها بعد تموم میشه. چکاوت، آنبوردینگ، بازپرداخت، KYC و امضای سند همه همین شکل رو دارن، ولی معمولاً تصادفی ساخته میشن: یه job، یه listener، یه کامند زمانبندیشده و یه ستون status با نُه حالت. به گفتهٔ نویسنده اون چیزی که ساختی یه ماشین حالته، فقط پخششده بین پنج فایل.
تعریف یه workflow تو این پکیج فقط یه اسم و لیست مرتب از کلاسهای step ـه. هر step با execute() یه کار انجام میده و یه StepResult برمیگردونه که به موتور میگه بعدش چی بشه، و خودش هم اعلام میکنه چقدر حاضره منتظر سیگنال بمونه و چند بار اجازهٔ اجرای مجدد داره. دادهها توی WorkflowContext جابهجا میشن؛ یه کیف دادهٔ immutable که مستقیم توش نمینویسی و فقط از طریق خروجی step آپدیت میشه.
جالبترین بخش، انتظاره. await یعنی نمونه با وضعیت awaiting توی دیتابیس پارک میشه و هیچ ورکری اشغال نمیمونه؛ سفارش میتونه ربع ساعت اونجا بمونه و هزینهش فقط یه ردیف دیتابیسه. تایماوت هر step با یه job تأخیری محافظت میشه که به همون step تگ خورده، پس تایماوت قدیمی نمیتونه stepای رو بکشه که سفارش خیلی وقته ازش رد شده. ریتری هم per-instance ـه، با backoff فاصله میگیره و با هر step تازه ریست میشه.
برای اینکه وبهوک بدونه کدوم نمونه رو بیدار کنه، هر instance با یه جفت aggregateId و aggregateType به خود سفارش گره میخوره؛ لازم نیست ID نمونه رو جایی مثل متادیتای Stripe نگه داری یا جدول همبستگی بسازی. اگه وبهوک زودتر از پارک شدن step برسه، موتور سیگنال رو بافر میکنه و مسابقه رو نمیبازی، و هر تحویل — چه مچ بشه چه نه — توی جدول workflow_signals لاگ میشه. انشعاب هم با goto انجام میشه و ترتیب stepها تو تعریف، تعیین میکنه دو شاخه کجا دوباره به هم میرسن.
وقتی یه step بعد از تموم شدن ریتریهاش شکست بخوره، موتور سراغ stepهای تکمیلشدهای میره که CompensatingStep هستن و به ترتیب معکوس جبرانشون میکنه: اول بازپرداخت، بعد آزاد کردن موجودی. sleep هم ایمیل پیگیری دو روز بعد رو بدون کرونِ اسکنکننده ممکن میکنه. برای عملیات، workflow:show میگه سفارش کجا پارک شده و چه سیگنالهایی مصرف نشده، و workflow:tick روی زمانبند، خوابیدههایی که job تأخیریشون گم شده رو دوباره صف میکنه.
نکات کلیدی:
- فرایندهای بلندمدت (چکاوت، KYC، بازپرداخت) در عمل ماشین حالتن؛ موتور workflow همون رو صریح میکنه
- هر step یه کلاس معمولیه که از کانتینر ریزالو میشه و مستقل تست میشه
- WorkflowContext تغییرناپذیره و فقط از طریق خروجی step یا دادهٔ سیگنال آپدیت میشه
- انتظار برای وبهوک یا تأیید انسانی، نمونه رو تو دیتابیس پارک میکنه و ورکر رو نگه نمیداره
- سیگنالِ زودرس بافر میشه و همهٔ تحویلها لاگ میشن؛ اولین جای دیباگ سفارش گیرکرده همین لاگه
- شکست بعد از ریتری، جبرانسازی saga رو به ترتیب معکوس اجرا میکنه؛ compensate() بیرون از قفل دیتابیسه، پس idempotency key لازم داره
- برای هر گذار یه ایونت دامنه فایر میشه که گزارشگیری و هشدار رو مجانی میکنه
- لیست stepها موقع start() روی نمونه اسنپشات میشه، پس دیپلوی وسط راه سفارشهای در جریان رو خراب نمیکنه




