Workflow SDK: کد معمولی، اجرای پایدار
خلاصهٔ کاملتر
نویسندهٔ این پست تو ورسل میگه حدود شش ماه روی فورکی از Temporal کار کرده و آخرش بیخیالش شده و از صفر یه فریمورک جدید نوشته: Workflow SDK. حرف اصلیش اینه که ورکفلو در واقع یه DAG هست، یعنی گرافی که میگه چه کاری بعد از چه کاری اجرا شه، و ما از قبل ابزار بیان همچین چیزی رو داریم: خود زبان برنامهنویسی. پس بهجای اینکه گراف رو دستی بکشی، همون کد ترتیبی معمولی رو مینویسی و موتور زیرش پایدارش میکنه.
تو عمل یه فایل مینویسی. عبارت "use workflow" بالای تابع میگه این ارکستریتوره و "use step" میگه این تیکه، واحد کار جانبیه (مثلاً کال زدن به Stripe). بقیهش همون await و try/catch و Promise.all و حلقه و شرط معمولیه، و کامپایلر از روی همین دایرکتیوها کد رو به باندل ورکفلو و باندل استپ میشکنه:
export async function processOrderWorkflow(orderId: string) {
"use workflow";
const order = await fetchOrder(orderId);
await chargePayment(order);
return { orderId, status: "completed" };
}خطای گرفتهنشده توی استپ خودکار ریتری میشه؛ با FatalError جلوش رو میگیری و با fn.maxRetries تعدادش رو تنظیم میکنی. نویسنده میگه سه تا مفهوم Temporal یعنی signal و query و update رو هیچوقت نمیتونسته بدون وایتبرد توضیح بده و همین براش بوی بد DX میداده، برای همین هر سه رو با یه مفهوم واحد به اسم hook عوض کرده. createWebhook() روی همین hook سواره و وسط یه ران یه URL واقعی میسازه که ران تا وقتی کسی بهش ریکوئست بزنه پارک میمونه، از چند ثانیه تا چند هفته:
const webhook = createWebhook();
await emailManager(expense.managerEmail, webhook.url);
const request = await webhook;
const { approved } = await request.json();فرق دومش با موتورهای سنتی اینه که یه کتابخونهست، نه پلتفرم. به گفتهٔ نویسنده این شکل رو از DBOS گرفتن: ارکستریتور جدایی برای self-host کردن یا پول دادن وجود نداره و فقط همون اپ و دیتابیس و صفی که داری کافیه. رانتایم فقط با یه اینترفیس به اسم World حرف میزنه که استوریج و صف و احراز هویت و استریم رو پوشش میده، پس میتونی زیرش Postgres یا Turso یا Durable Objects بذاری بدون اینکه کد ورکفلو دست بخوره.
نویسنده صادقانه میگه بعضی انتخابها به شکل اجرای کد روی ورسل گره خورده. واضحترینش نسخهبندیه: هر ران به همون دیپلویی پین میشه که باهاش شروع شده، پس عوضکردن کد ورکفلو رانهای در جریان رو نمیشکنه. این راهحل فقط چون ورسل دیپلویهای immutable رو مدتها نگه میداره ساده بوده و ورلد رسمی Postgres هنوز این کارو نمیکنه.
مسئلهٔ بعدی سرعته. هر استپ یه رفتوبرگشت شبکه و یه سفر روی صف داره تا نتیجهش پایدار ثبت شه، و همین باعث میشه آدم استپها رو جیرهبندی کنه. هدف تیم اینه که استپ انقدر ارزون شه که مثل صدا زدن یه تابع معمولی بهش فکر نکنی. نسخهٔ v5 که موقع نوشتن پست بتا بوده تا ۵ برابر سریعتر شده، بدون اینکه API سمت کاربر عوض شه.
نکات کلیدی:
- "use workflow" ارکستریتور رو مشخص میکنه و "use step" واحد کار جانبی رو؛ گراف اجرا از خود کنترلفلوی کد درمیاد و فایل DAG جدایی لازم نیست.
- مفهوم hook جای هر سه primitive تمپورال یعنی signal و query و update رو گرفته.
- createWebhook() وسط یه ران یه URL واقعی و قابلفراخوانی میسازه و ران میتونه چند ثانیه تا چند هفته روش پارک بمونه.
- هر ران به دیپلوی شروعکنندهش پین میشه، برای همین تغییر کد باعث خطای non-determinism روی رانهای نیمهتمام نمیشه.
- زیرساخت قابلتعویضه: Redis یا Kafka برای استریم، Postgres یا Turso یا Durable Objects برای دوام، Vercel Queues یا SQS برای صف.
- سرور ورکفلو ورسل بدون حالت و فقط یه CRUD APIه؛ تمام منطق توی کتابخونهٔ اپاچیلایسنس سمت کلاینته.
- نسخهٔ v5 در حالت بتا تا ۵ برابر بهبود سرعت داره و با npm install workflow@beta قابل تسته.




