Workflow Automation: State Machines, Saga Pattern ותהליכים ארוכי-טווח
מאת צוות מדיה דיל · 06.08.2026 · Automation · 10 דק׳
מדריך ארכיטקטוני ל-Workflow Automation: מנועי workflow, Saga Pattern לביטול פעולות, תמיכה באישור אנושי ו-versioning של תהליכים פעילים.
חברת לוגיסטיקה בונה תהליך אונבורדינג לספקים חדשים: קבלת מסמכים, אימות משפטי, הגדרת תנאי תשלום, יצירת חשבון במערכת, ושליחת הודעה לצוות הרכש. בהתחלה זה נראה כמו רצף פשוט של שלבים, אז מישהו כותב סקריפט ליניארי שמריץ אותם אחד אחרי השני. אחרי חודשיים, מתגלה שהתהליך צריך לחכות לאישור אנושי באמצע (שיכול לקחת ימים), לטפל בספק שמבטל תהליך באמצע, ולתמוך בכמה תהליכים מקבילים לספקים שונים בלי שהם "יתבלבלו" זה בזה. סקריפט ליניארי לא שורד את זה. Workflow Automation, כשהיא נבנית נכון, היא בדיוק המנגנון שמנהל תהליכים ארוכי-טווח, מרובי-שלבים, עם המתנות, הסתעפויות ו-state שצריך להישמר לאורך זמן. המאמר הזה בוחן את הארכיטקטורה שמאחורי מנועי workflow אמינים, ואת ההבדל בין תהליך שרץ פעם אחת בהצלחה לתהליך שממשיך לעבוד נכון גם כשמשהו משתבש באמצע.
למה סקריפט ליניארי לא מספיק
סקריפט רגיל שמריץ שלבים ברצף מניח דבר אחד קריטי: שהוא ירוץ ברצף אחד, מההתחלה ועד הסוף, בלי הפרעה. ברגע שהתהליך העסקי כולל המתנה ממושכת (אישור אנושי, תשלום שמאושר בנפרד, תלות בתהליך חיצוני שלוקח שעות או ימים), ההנחה הזו קורסת — אי אפשר להשאיר process רץ בזיכרון ומחכה שלושה ימים לתשובה. ואם השרת עצמו מופעל מחדש (דיפלוי, קריסה, סקיילינג), כל התהליכים שהיו "באמצע" נעלמים לחלוטין, בלי שום דרך לשחזר איפה הם היו. זו לא בעיה תיאורטית — דיפלוי רגיל שקורה כמה פעמים בשבוע יכול "לתקוע" עשרות תהליכים באמצע במקרה הגרוע ביותר, ובלי מנגנון שחזור, הפתרון היחיד נותר תיקון ידני שורה-שורה במסד הנתונים.
הפתרון הארכיטקטוני הוא להפריד בין "לוגיקת התהליך" לבין "מנוע הביצוע": התהליך עצמו מתואר כמכונת מצבים (state machine) או כגרף של שלבים, וכל שלב שהושלם נשמר באופן מתמיד (persistent) במסד נתונים או בתשתית ייעודית לפני שממשיכים לשלב הבא. כשהמערכת מתאתחלת מחדש, היא לא מריצה מחדש את כל התהליך מההתחלה — היא בודקת איפה כל instance של תהליך נמצא, ומחכה בדיוק מהנקודה שבה הוא הפסיק.
מנועי Workflow: Temporal, AWS Step Functions ופתרונות מותאמים
Temporal (וקודמו Cadence) הוא מנוע workflow שמאפשר לכתוב תהליכים ארוכי-טווח כקוד רגיל (לא כ-JSON או DSL), כשהמנוע עצמו אחראי ל-persistence, retry, ו-recovery אוטומטיים. היתרון המרכזי: הקוד נראה כמו פונקציה סינכרונית רגילה (await activity1(); await activity2()), אבל מאחורי הקלעים כל צעד נשמר, וגם אם התהליך "מת" באמצע השלב השלישי מתוך עשרה, הוא ממשיך משם בדיוק כשהוא מתאתחל מחדש — בלי לחזור על שלבים שכבר הצליחו. AWS Step Functions מציע גישה דומה אבל מבוססת JSON state machine במקום קוד, מנוהלת לגמרי בענן, ומשתלבת טבעית עם שאר שירותי AWS — נוחה מאוד כשכל התשתית כבר ב-AWS, אבל פחות גמישה כשצריך לוגיקה מורכבת שקשה לבטא ב-JSON.
לצד אלה, יש מקום לפתרונות מותאמים פשוטים יותר: state machine עצמאי שמנוהל ידנית מעל מסד נתונים רגיל (טבלת workflow_instances עם עמודת current_state), עם worker שבודק תקופתית אילו instances מוכנים להתקדם. הגישה הזו פחות אלגנטית ומוסיפה עבודת פיתוח, אבל מתאימה כשהמורכבות של Temporal לא מוצדקת עדיין, או כשיש כבר תשתית קיימת שלא כדאי להחליף. חשוב להיות כנים עם עצמכם לגבי כמות ההשקעה הנדרשת בגישה הזו: כל אחת מהיכולות ש-Temporal נותן "בחינם" — retry, versioning, event history — צריכה להיבנות ידנית, וזה בקלות יכול להפוך לפרויקט תשתית בפני עצמו במקום להתמקד בלוגיקה העסקית.
Compensating Actions: מה קורה כשצעד באמצע נכשל
בתהליכים מרובי-שלבים שנוגעים במערכות חיצוניות (חיוב כרטיס אשראי, יצירת הזמנה, שריון מלאי), כשל בשלב מאוחר דורש "לבטל" שלבים קודמים שכבר הצליחו — לא ניתן פשוט לעצור ולהשאיר את המערכת במצב חלקי. זהו דפוס ה-Saga: לכל צעד שיש לו side effect חיצוני מוגדר compensating action מקביל (לביטול חיוב יש refund, ליצירת הזמנה יש cancel_order, לשריון מלאי יש release_inventory). אם שלב כלשהו נכשל, המנוע מריץ אוטומטית את פעולות הביטול של כל הצעדים שכבר הצליחו, בסדר הפוך.
התכנון הזה חייב להיעשות מראש, לא כתיקון אחרי תקלה — כי אם צעד ג' יכול להיכשל, השאלה "מה קורה לצעדים א' וב' שכבר רצו" חייבת תשובה ברורה לפני שהתהליך מגיע לפרודקשן, לא אחרי שלקוח מגלה שהוא חויב על הזמנה שמעולם לא נוצרה. שווה גם לזכור שלא כל compensating action באמת "מבטלת" את הפעולה המקורית באופן מושלם — refund על חיוב שכבר בוצע יכול לקחת ימים להשלים בפועל אצל חברת האשראי, ולכן חשוב לתעד את ההבדל בין "בקשת ביטול נשלחה" לבין "הביטול הושלם בפועל" כדי לא להטעות משתמשים או מערכות downstream לגבי המצב האמיתי.
הבחנה בין Human-in-the-Loop לתהליכים אוטומטיים לחלוטין
הרבה תהליכים עסקיים דורשים המתנה לפעולה אנושית — אישור מנהל, בדיקה ידנית, החלטה עסקית. מנוע workflow טוב צריך לתמוך בזה כסוג צעד ראשון-מדרגה, לא כפתרון עוקף. הדפוס הנכון הוא ליצור "משימה" ממתינה (task) שמופיעה בממשק לאדם הרלוונטי, עם timeout מוגדר (אם אין תגובה תוך X ימים, מסלימים להתראה או לברירת מחדל), והתהליך נשאר "מוקפא" עד שהמשימה מסומנת כהושלמה — בלי שום polling אקטיבי מהתהליך עצמו, אלא callback שמעיר אותו כשהתשובה מגיעה. הפתרון הזה חוסך משאבי מחשוב יקרים בהשוואה לפולינג תקופתי, וגם מבטל עומס מיותר על המסד נתונים שמנהל את ה-state של אלפי תהליכים ממתינים בו-זמנית.
נקודה שקל לפספס: תהליך שמחכה לאישור אנושי יכול לחכות ימים או שבועות, ובמהלך הזמן הזה עדיין צריך שיהיה ניתן לבטל אותו, לשנות פרמטרים, או לבדוק את הסטטוס שלו — לכן חשוב שכל instance של תהליך יהיה ניתן לשאילתה (queryable) בכל רגע נתון, לא רק בסוף הריצה.
Versioning של תהליכים פעילים
אחת הבעיות הכי לא אינטואיטיביות בפרודקשן: מה קורה כשמעדכנים את קוד ה-workflow בזמן שיש עשרות instances פעילים שרצים לפי הגרסה הישנה? אם המנוע לא תומך ב-versioning מפורש, שינוי בקוד עלול לגרום ל-instances קיימים "לקפוץ" לנקודה לא צפויה בגרסה החדשה, עם השלכות בלתי צפויות. מנועים בשלים כמו Temporal פותרים את זה עם API ייעודי ל-versioning שמאפשר להריץ תנאי מפורש ("אם instance התחיל לפני גרסה 3, התנהג כך; אחרת, כך"), כך שתהליכים ישנים ממשיכים להתנהג בדיוק כמו שציפו כשהם התחילו, וחדשים מקבלים את הלוגיקה המעודכנת.
ניטור ו-Debugging של תהליכים ארוכי-טווח
כשתהליך יכול לרוץ ימים או שבועות, כלי דיבוג רגילים (breakpoints, stack traces) לא רלוונטיים. הדרישה המרכזית היא event history מלא ונגיש לכל instance — כל צעד שהתבצע, מתי, עם אילו פרמטרים, ומה התוצאה — כך שאפשר "לשחזר" את מסלול החיים המלא של תהליך ספציפי בלי לחפור בלוגים מפוזרים. Temporal מספק את זה built-in (Workflow History), ופתרונות מותאמים צריכים לבנות את זה במפורש כטבלת audit log.
חשוב גם dashboard תפעולי שמראה כמה instances תקועים בכל שלב, לכמה זמן, ומה שיעור הכישלון בכל שלב — זה מאפשר לזהות מהר מאוד אם שינוי אחרון גרם לצוואר בקבוק חדש בתהליך, בלי לחכות שלקוחות יתלוננו. שווה גם להגדיר alert אוטומטי על instances שחורגים משמעותית מזמן הריצה הצפוי — תהליך אונבורדינג שאמור להסתיים תוך יומיים אך תקוע כבר שבועיים הוא כנראה תקלה שדורשת התערבות, לא רק "ממתין" כרגיל.
דוגמת קוד: Workflow עם Compensation ב-Temporal
הנה שלד שממחיש איך נראה תהליך רב-שלבי עם ביטול אוטומטי בכשל, בסגנון Temporal — כל activity נרשמת לרשימת ביטולים לפני שהיא מתבצעת, כך שאם צעד מאוחר יותר נכשל, כל מה שכבר בוצע מתבטל בסדר הפוך:
export async function onboardSupplierWorkflow(input: SupplierInput) {
const compensations: (() => Promise)[] = [];
try {
const supplierId = await createSupplierRecord(input);
compensations.push(() => deleteSupplierRecord(supplierId));
await requestLegalApproval(supplierId); // waits for human task, can take days
compensations.push(() => revokeLegalApproval(supplierId));
const paymentTermsId = await setupPaymentTerms(supplierId, input.terms);
compensations.push(() => teardownPaymentTerms(paymentTermsId));
await notifyProcurementTeam(supplierId);
return { status: 'completed', supplierId };
} catch (err) {
for (const compensate of compensations.reverse()) {
await compensate();
}
throw err;
}
}
הקריאה ל-requestLegalApproval היא בדיוק סוג הצעד שממחיש למה סקריפט ליניארי לא שורד: הפונקציה הזו יכולה "לישון" ימים שלמים בהמתנה לפעולה אנושית, ובזמן הזה כל ה-state של התהליך — כולל רשימת ה-compensations שכבר נצברה — נשמר על ידי המנוע ולא בזיכרון תהליך שעלול לקרוס. כשהאישור מגיע (או נדחה), התהליך ממשיך בדיוק מהנקודה הזו, גם אם השרת שהריץ אותו במקור כבר לא קיים יותר.
תזמון ו-Retry ברמת השלב הבודד
יתרון נוסף של מנועי workflow ייעודיים הוא retry policy עצמאי לכל שלב בנפרד, במקום מדיניות גורפת אחת לכל התהליך. שלב שקורא ל-API חיצוני לא יציב יכול להיות מוגדר עם עד עשרה ניסיונות עם backoff אקספוננציאלי, בעוד ששלב שמעדכן מסד נתונים פנימי יציב יכול להסתפק בשני ניסיונות בלבד. ניתן גם להגדיר timeout נפרד לכל שלב — הבחנה בין "כמה זמן מותר לשלב הבודד לרוץ" (activity timeout) לבין "כמה זמן מותר לכל התהליך לרוץ" (workflow timeout, שיכול להיות ימים או שבועות עבור תהליכים עם המתנה אנושית) היא קריטית ולעיתים קרובות מפוספסת בפתרונות מותאמים תוצרת בית, שנוטים להשתמש בערך אחד גורף שמתאים לחלק מהמקרים ושובר את השאר.
סיכום
Workflow Automation אמיתית שונה מהותית מהרצת סקריפט: היא דורשת persistence של state, טיפול מובנה בכשלים חלקיים דרך compensating actions, תמיכה מובנית בהמתנה אנושית, versioning של תהליכים פעילים, ו-observability שמאפשר לשחזר כל צעד בדיעבד. הבחירה בין מנוע ייעודי (Temporal, Step Functions) לפתרון מותאם תלויה בהיקף ובמורכבות, אבל העקרונות זהים בשני המקרים — persistence לפני התקדמות, טיפול מפורש בכשל חלקי, ותצפית מלאה על מסלול החיים של כל instance. תהליכים עסקיים מרובי-שלבים שדורשים גם אישורים אנושיים בדרך, כדאי לקרוא גם על Approval Workflow Automation שמתמקד בדיוק בהיבט הזה.
תגיות: Workflow Automation · Temporal · AWS Step Functions · Saga Pattern · State Machine · Human-in-the-Loop · Compensating Actions