התשובה הקצרה
השאלה "Flow או Apex" מקבלת תשובה שגויה כשבודקים אותה לפי נוחות הכתיבה או זמינות מפתח. התשובה הנכונה תלויה בארבעה גורמים טכניים: כמה רשומות עוברות בטרנזקציה אחת, האם הלוגיקה חייבת Atomicity מלאה, כמה מורכבים התנאים והענפים, ומי יתחזק את הרכיב בעוד שנה. Flow הוא הבררת מחדל הנכונה לרוב האוטומציות העסקיות — אבל יש נקודות מעבר ברורות שבהן המשך עבודה עם Flow יוצר סיכון תפעולי ולא רק "קוד פחות אלגנטי".
המאמר הזה עוסק בהחלטת הבחירה עצמה: איך לזהות מראש שהלוגיקה מצדיקה Apex, ואיך למנוע מצב שבו הבחירה נעשית כברירת מחדל ולא כשיקול דעת. סוגיית ניקוי Flow-ים ותהליכי אוטומציה קיימים שכבר הצטברו כחוב טכני נדונה במאמר נפרד ואינה חלק מהדיון כאן.
מה בפועל מבדיל בין השניים ברמת הפלטפורמה
Flow הוא מנוע Declarative שמתורגם בזמן ריצה להוראות שמבצעות DML ו-SOQL בשם המשתמש, בעוד Apex הוא קוד מקומפל שרץ תחת אותם Governor Limits אך עם שליטה ישירה בסדר הפעולות. ההבדל המעשי הראשון הוא Bulkification: מפתח Apex בונה במפורש לולאה שאוספת את כל הרשומות למערך אחד ומריצה DML בודד, בעוד ב-Flow קל לבנות Loop שמבצע פעולת DML או שאילתה בכל איטרציה בנפרד — דפוס שמגיע ל-101 השאילתות המותרות הרבה יותר מהר.
ההבדל השני הוא שליטה בטרנזקציה. Apex מאפשר Savepoint ו-Database.rollback לביטול חלקי, טיפול ב-DmlException ברמת רשומה בודדת דרך Database.insert(list, false), ולוגיקה מותנית מורכבת בלי מגבלת עומק ענפים. ב-Flow, טיפול בשגיאות מוגדר ברמת Fault Path לכל אלמנט, וזה עובד היטב לתרחישים ליניאריים אך נהיה קשה למעקב כשיש יותר מכמה נתיבי כשל מקבילים.
מסגרת ההחלטה: ארבעה מבחנים לפני שבוחרים כלי
מבחן הנפח
כלל אצבע: אם התהליך רץ על רשומה בודדת כתוצאה מפעולת משתמש (יצירת ליד, שינוי סטטוס הזדמנות), Flow מספיק כמעט תמיד. אם התהליך רץ על עשרות עד אלפי רשומות בבת אחת — עדכון תקופתי, טיפול ב-Batch שמגיע מאינטגרציה, ניקוי נתונים מתוזמן — Apex עם Batchable או Queueable הוא הבחירה הבטוחה, כי הוא מעניק שליטה מלאה ב-Bulkification ובניהול Governor Limits מול נפח משתנה.
מבחן ה-Atomicity
יש לשאול: אם חלק מהעדכון נכשל, מותר שהחלק האחר יישאר שמור? אם התשובה "לא" — למשל עדכון הזמנה ויצירת רשומת חיוב שחייבים לקרות יחד — Apex עם Savepoint הוא הדרך הנכונה להבטיח את זה. Flow לא נותן Rollback מלא בין אלמנטים בלי בנייה ידנית ומורכבת של לוגיקת פיצוי.
מבחן מורכבות הענפים
Flow עם יותר מ-6-8 Decision Elements מקוננים הופך לקשה לקריאה ויקר לבדיקה, גם אם כל ענף בודד פשוט. כשמורכבות הלוגיקה העסקית עולה על זה, כתיבת אותה לוגיקה כפונקציית Apex מתועדת עם בדיקות יחידה (@isTest) בדרך כלל זולה יותר לתחזוקה, גם אם זמן הכתיבה הראשוני ארוך יותר.
מבחן התחזוקה והבעלות
יש לשאול מי יתחזק את הרכיב בעוד שנה, לא מי בונה אותו עכשיו. אם צוות ה-Admin הוא זה שיצטרך לעדכן את הכללים העסקיים באופן שוטף — כמו שינוי תנאי הנחה או ערכי סף — Flow עדיף גם אם Apex טכנית "נקי" יותר, כי הוא נגיש לעדכון בלי מחזור פריסה. אם השינויים דורשים ידע בסכימת הנתונים ובבדיקות רגרסיה, Apex הוא הבחירה הנכונה גם אם יש רק צוות פיתוח קטן שיתחזק אותו.
טבלת החלטה
| קריטריון | בחר Flow | בחר Apex |
|---|---|---|
| נפח רשומות בטרנזקציה בודדת | עד עשרות בודדות | מאות עד אלפים |
| דרישת Atomicity בין כמה אובייקטים | לא קריטית | קריטית — נדרש Rollback מלא |
| מספר ענפי החלטה | עד כ-6-8 | מעל כך, או לוגיקה רקורסיבית |
| קצב שינוי הכללים העסקיים | תדיר, ע"י Admin | נדיר, דורש בדיקות רגרסיה |
| צורך בקריאה ל-API חיצוני מורכב | קריאה בודדת פשוטה (HTTP Callout) | לוגיקת Retry, Auth מורכב או Batch |
| דרישת בדיקות אוטומטיות (CI) | מוגבלת | מלאה, @isTest עם Coverage |
| שילוב עם Scheduled Job קבוע | לא מתאים ישירות | טבעי דרך Schedulable |
תרחיש דוגמה: חברת ציוד רפואי עם תהליך אישור הזמנות
חברת ציוד רפואי בינונית עם כ-40 נציגי מכירות הפעילה Flow אחד לתהליך אישור הזמנה: בדיקת מלאי, חישוב הנחה, יצירת רשומת אישור ושליחת התראה למנהל. בהתחלה זה עבד היטב על הזמנה בודדת. אחרי חצי שנה נוסף תרחיש חדש — ייבוא הזמנות קבוצתי מקובץ מאינטגרציה עם ה-ERP, שיוצר בין 200 ל-800 הזמנות בו-זמנית.
ה-Flow, שהופעל דרך Record-Triggered Flow ברמת "לכל רשומה", ביצע שאילתת בדיקת מלאי בתוך כל הפעלה בנפרד. עם ייבוא של 500 הזמנות, המערכת חצתה את מגבלת 100 השאילתות בטרנזקציה בודדת וההזמנות נכשלו בלי הודעת שגיאה ברורה למשתמש. הצוות זיהה שהבעיה אינה ב-Flow עצמו אלא בהתאמה בין תהליך שתוכנן לרשומה בודדת לבין תרחיש נפח שלא היה קיים בעת הבנייה.
הפתרון לא היה לזרוק את ה-Flow. הצוות פיצל את הלוגיקה: Flow נשאר אחראי על התהליך הידני של הזמנה בודדת (מבחן הנפח נמוך, יש צורך בעדכון תדיר של כללי הנחה על ידי Admin), ואילו תהליך הייבוא הקבוצתי הועבר ל-Apex Batch Job שמבצע Bulkification מלא, בודק מלאי בשאילתה אחת מרוכזת ומריץ DML יחיד לכל הרשומות. שני המנגנונים קוראים לאותה שכבת לוגיקה עסקית משותפת (Apex Class אחת שגם ה-Flow קורא לה דרך Invocable Method), כדי שכלל ההנחה לא יתוחזק פעמיים.
סיכונים נפוצים ופעולות מניעה
| סיכון | איך זה נראה בפועל | פעולת מניעה |
|---|---|---|
| Flow על נפח שגדל בהדרגה | תהליך עבד חצי שנה ואז נכשל בשקט על Governor Limits | לבדוק את הנפח הצפוי מראש ולתכנן נקודת מעבר ל-Apex לפני שמגיעים לגבול |
| כפילות לוגיקה עסקית ב-Flow וב-Apex | שני מקומות מחשבים הנחה בצורה שונה | לרכז חישוב עסקי בשכבת Apex משותפת שגם Flow קורא לה |
| Trigger Order לא צפוי | כמה Flow-ים ו-Trigger על אותו אובייקט מתנגשים | Trigger Handler מרכזי אחד ב-Apex לכל אובייקט קריטי |
| טיפול חלקי בשגיאות ב-Flow מורכב | חלק מהרשומות מתעדכנות וחלק לא, בלי נראות | להעביר תהליכים הדורשים Atomicity ל-Apex עם Savepoint |
| Apex ללא בדיקות מספיקות | שינוי קטן שובר תהליך קריטי בפריסה הבאה | לדרוש Coverage אמיתי ולא רק אחוז פורמלי, כולל תרחישי כשל |
Checklist להחלטה לפני בנייה
- ☐ נבדק הנפח הצפוי לטווח שנה, לא רק המצב הנוכחי
- ☐ הוגדר אם התהליך דורש Atomicity בין כמה אובייקטים
- ☐ נספרו ענפי ההחלטה הצפויים בלוגיקה
- ☐ ידוע מי יתחזק את הרכיב ובאיזו תדירות ישתנו הכללים
- ☐ נבדק אם קיימת כבר לוגיקה דומה ב-Apex או ב-Flow אחר על אותו אובייקט
- ☐ הוגדר Trigger Order אם יש כמה מנגנוני אוטומציה על האובייקט
- ☐ אם נבחר Apex — הוגדרו תרחישי בדיקה כולל כשל חלקי
- ☐ אם נבחר Flow — הוגדר Fault Path לכל אלמנט קריטי
איך זה מתחבר לארכיטקטורה הרחבה
בחירת הכלי הנכון לאוטומציה בודדת היא רק שכבה אחת בתוך תמונה רחבה יותר של ארכיטקטורת CRM, שבה גם מודל הנתונים וגם ההרשאות משפיעים על מה ש-Flow או Apex יכולים בכלל לגעת בו. כשהאוטומציה חוצה גבול לארגון חיצוני — למשל בדיקת מלאי מול ERP בזמן אמת — הבחירה בין Flow ל-Apex משתלבת גם בשיקולי דפוסי אינטגרציה ובשאלה איך Salesforce מתחבר ל-ERP מבחינת Latency וטיפול בכשל.
בארגונים שמפעילים כמה Org-ים, יש לבדוק גם אם הלוגיקה העסקית זהה בכולם — נושא שנדון במדריך Single Org מול Multi Org ומשפיע על השאלה אם שווה למרכז את הלוגיקה ב-Apex Package משותף.
סיכום
הבחירה בין Flow ל-Apex אינה שאלה של מיומנות צוות או טעם אישי, אלא תוצאה של ארבעה מבחנים טכניים: נפח, Atomicity, מורכבות ענפים ותדירות שינוי. Flow הוא ברירת המחדל הנכונה לרוב האוטומציות שנוגעות ברשומה בודדת ומשתנות בתדירות גבוהה. Apex נדרש כשיש נפח משמעותי, כשצריך שליטה מלאה בטרנזקציה, או כשהמורכבות הלוגית עוברת את הסף שעדיין ניתן לתחזק דרך ממשק Declarative. ארגון שממסד את המבחנים האלה כחלק מתהליך העבודה — ולא משאיר אותם לשיקול דעת אד-הוק של כל מפתח — חוסך את רוב המקרים שבהם אוטומציה שעבדה היטב בהתחלה נשברת בשקט כשהנפח גדל.
