איך לנהל שינוי schema באירועים בלי לשבור צרכנים קיימים
אינטגרציות · אוקטובר 2026
מדריך לניהול גרסאות schema באירועים: מיפוי צרכנים, תאימות, expand and contract, בדיקות חוזה, אירועים היסטוריים, פריסה ומדדים.
קטגוריה: אינטגרציות | תאריך: אוקטובר 2026
במערכת מבוססת אירועים, producer אחד יכול לפרסם שינוי שמשמש כמה צרכנים: תהליך תפעולי, דוח BI, אינדקס חיפוש, מערכת התראות או אינטגרציה למערכת ליבה. שינוי קטן ב-payload — שדה חדש, שם אחר, טיפוס שונה או משמעות עסקית מעודכנת — עלול להשפיע על כל אחד מהם בצורה אחרת.
הקושי אינו רק לגרום להודעה לעבור validation. צריך לאפשר ליצרן ולצרכנים להתקדם בקצב שונה, לשמור על משמעות עסקית עקבית, לטפל באירועים ישנים שנשמרו בתור או בארכיון, ולדעת מתי ניתן להסיר תמיכה בגרסה קודמת. לכן שינוי schema הוא תהליך ניהולי ותפעולי, לא רק עריכה של JSON.
מהו החוזה של אירוע?
ה-schema מתאר בדרך כלל את המבנה: שמות שדות, טיפוסים, שדות חובה, ערכים אפשריים וקינון. אבל חוזה האירוע רחב יותר. הוא כולל גם:
- מה קרה בעולם העסקי ומתי.
- מי הישות שאליה האירוע שייך ומהו המזהה היציב שלה.
- האם האירוע מתאר עובדה שכבר קרתה או בקשה לביצוע פעולה.
- אילו ערכים מייצגים מצב סופי, ביניים או חריג.
- האם שדה חסר פירושו ״לא ידוע״, ״לא רלוונטי״ או ערך ברירת מחדל.
- אילו כללי סדר, כפילות ותיקון חלים על האירוע.
לדוגמה, הוספת שדה approvalStatus אינה מספיקה אם לא הוגדר האם הוא משקף אישור של כל הבקשה, של שורה אחת או של שלב מסוים. צרכן שמפרש את השדה אחרת יכול לבצע פעולה תקינה מבחינה טכנית אך שגויה מבחינה עסקית.
לפני שינוי, כתבו תיאור קצר של משמעות האירוע ושל כל שדה שמשפיע על החלטה. מסמך כזה משלים schema טכני ומאפשר לבעל התהליך לאשר שהמשמעות נשמרת.
לזהות את הצרכנים לפני שמשנים
אירועים נועדו לנתק בין producer לצרכן, אך הניתוק אינו אומר שאין תלות. הוא רק מעביר את התלות לחוזה. לפני שמשנים schema צריך לבנות מפת צרכנים מעשית:
- אילו שירותים, עבודות רקע, דוחות ומערכות קוראים את סוג האירוע.
- איזו גרסה כל צרכן יודע לעבד.
- באילו שדות והנחות עסקיות הוא משתמש בפועל.
- מי הבעלים הטכני והעסקי של כל צרכן.
- מה יקרה אם הוא יקבל שדה לא מוכר, ערך חדש או אירוע שאינו ניתן לפענוח.
רישום בקטלוג או ב-schema registry יכול לעזור, אך אין להסתמך עליו בלבד אם ההרשמה אינה מחייבת. לוגים ונתוני consumer groups יכולים לחשוף צרכנים פעילים, אך חלון קצר עלול לפספס תהליך חודשי או צרכן התאוששות שמופעל רק בתקלה. משלבים תיעוד, תצפית ואישור של בעלי המערכות.
אם אין דרך לזהות צרכן או בעלים, זהו סיכון שצריך לפתור לפני הסרת תאימות. עצם העובדה שלא התקבלה תלונה אינה הוכחה שהגרסה הישנה אינה בשימוש.
לסווג את השינוי לפי השפעה אמיתית
לא כל שינוי דורש גרסה חדשה, ולא כל תוספת היא בטוחה. כדאי לסווג שינוי לפי השאלה האם צרכן קיים יכול להמשיך לקבל אותה משמעות ללא שינוי קוד.
שינויים שעשויים להיות תואמים
- הוספת שדה אופציונלי שצרכנים רשאים להתעלם ממנו.
- הוספת metadata שאינו משנה החלטה עסקית.
- הרחבת תיעוד או דיוק של מגבלה שכבר נאכפה.
גם כאן נדרשת בדיקה. מפענח קשיח עלול לדחות שדה נוסף, וצרכן שמשווה payload מלא עלול לזהות אותו כשינוי לא צפוי.
שינויים שעלולים להיות שוברים
- מחיקה, שינוי שם או שינוי טיפוס של שדה.
- הפיכת שדה אופציונלי לחובה.
- הוספת ערך חדש ל-enum כאשר צרכנים מניחים רשימה סגורה.
- שינוי יחידת מידה, מטבע, אזור זמן או משמעות של סטטוס.
- שינוי מזהה הישות או כלל הסדר.
- שינוי מאירוע עובדה לאירוע שמבקש פעולה, או להפך.
הוספת סטטוס חדש היא דוגמה חשובה. אם צרכן מכיר רק ״אושר״ ו״נדחה״, ערך ״ממתין לבדיקה״ אינו יכול לקבל אוטומטית את מסלול ברירת המחדל. התנהגות בטוחה יכולה להיות עצירת הרשומה המושפעת והעברתה לבירור.
לבחור אסטרטגיית גרסאות מפורשת
יש כמה דרכים נפוצות לנהל גרסאות. הבחירה תלויה במשך חיי האירועים, במספר הצרכנים וביכולת לתאם פריסה.
שדה גרסה בתוך האירוע
ה-event type נשאר קבוע ושדה כגון schemaVersion קובע כיצד לפענח את ה-payload. הגישה נוחה כאשר אותו ערוץ מכיל כמה גרסאות, אך כל צרכן חייב לטפל בגרסה לא מוכרת ולא להניח שהגרסה החדשה זהה לקודמת.
סוג אירוע או ערוץ חדש
מפרסמים למשל OrderApprovedV2 לצד הגרסה הקודמת. ההפרדה ברורה ומאפשרת מעבר הדרגתי, אך דורשת להחליט מי מפרסם את שתי הגרסאות, כמה זמן, וכיצד מונעים משני האירועים להפעיל אותה פעולה פעמיים.
מודל קנוני ומתאמים
כל גרסה מומרת למודל פנימי מוסכם לפני הלוגיקה העסקית. כך מרכזים המרות ומקטינים פיזור של תנאי גרסה. עם זאת, מתאם יכול לתרגם מבנה בלבד כאשר המשמעות ידועה; הוא אינו רשאי להמציא מידע שחסר בגרסה ישנה.
בכל אסטרטגיה מגדירים מראש תקופת תאימות, בעלים, מדד מעבר ותנאי הסרה. תמיכה כפולה ללא תאריך ובלי תצפית נוטה להפוך לחוב קבוע.
להעדיף expand and contract על החלפה מיידית
כאשר אפשר, מבצעים שינוי בשני שלבים:
- Expand: ה-producer מוסיף מידע חדש או מפרסם גרסה חדשה, בעוד החוזה הישן ממשיך לעבוד.
- מעדכנים צרכנים, בודקים שימוש ותוצאות עסקיות.
- Contract: רק לאחר שכל הצרכנים הידועים עברו והצטברו הוכחות, מפסיקים לפרסם או לקבל את המבנה הישן.
לדוגמה, במקום לשנות total ממספר לאובייקט עם מטבע, אפשר להוסיף תחילה totalAmount ו-currency, להשאיר זמנית את total, ולעדכן צרכנים. לפני הסרת השדה הישן בודקים שאין צרכן שקורא אותו ושאין אירועים ישנים שעדיין יידרשו לעיבוד.
אין לבצע dual publishing בלי זהות עסקית משותפת. אם V1 ו-V2 מייצגים אותו אירוע, הם צריכים לשאת Event ID או מזהה פעולה שמאפשר לצרכן שעבר בין גרסאות למנוע side effect כפול. המדריך על idempotency באינטגרציה מפרט כיצד להפריד בין ניסיון מסירה לבין הפעולה העסקית.
לבדוק תאימות ברמת החוזה והמשמעות
בדיקת schema אוטומטית יכולה לזהות מחיקת שדה או שינוי טיפוס, אך אינה יודעת תמיד שמשמעות של סטטוס השתנתה. לכן משלבים כמה שכבות:
- בדיקת schema: האם payload חדש עומד בכללים והאם שינוי עומד במדיניות התאימות.
- contract tests של צרכן: דוגמאות שהצרכן מספק ומראות אילו שדות ותוצאות הוא צריך.
- בדיקות המרה: כל גרסה מוכרת הופכת למודל הקנוני הצפוי.
- בדיקות החלטה עסקית: אותו תרחיש מוביל לסטטוס, משימה או עדכון נכונים.
- בדיקות ערכים לא מוכרים: enum חדש או גרסה לא מוכרת אינם מקבלים מסלול מסוכן כברירת מחדל.
כדאי לכלול אירוע תקין מכל גרסה פעילה, שדה אופציונלי חסר, שדה נוסף, ערך חדש, payload פגום, אירוע כפול ואירועים שמגיעים בסדר שונה. עבור שינוי במשמעות, בעל התהליך צריך לאשר את תוצאת הבדיקה ולא רק את המבנה.
לתכנן מה קורה לאירועים שכבר בדרך
בזמן פריסה יכולים להישאר אירועים ישנים בתור, ב-DLQ, ב-outbox או בארכיון. גם אם producer חדש מפרסם רק V2, consumer חדש עשוי לקבל V1 לאחר עיכוב או replay.
לכן צריך לענות מראש:
- כמה זמן אירועים יכולים להמתין לפני עיבוד.
- אילו גרסאות נשמרות בארכיון ולכמה זמן הן עשויות להיקרא מחדש.
- האם consumer החדש תומך ישירות בגרסה הישנה או משתמש בממיר.
- מה קורה לאירוע שלא ניתן להמיר ללא מידע נוסף.
- האם הודעות ב-DLQ חוזרות לקוד חדש ומהי גרסתן.
אין להשלים שדה חסר באמצעות ניחוש. אירוע שאי אפשר להמיר עובר למסלול חריג עם סיבה, בעלים ומידע נדרש לתיקון. בעת replay של אירועים, מקבעים גם את גרסת הממיר כדי שאותה הרצה תהיה ניתנת להסבר ולשחזור.
לפרוס בלי ליצור שתי אמיתות עסקיות
אפשר להריץ צרכן חדש במצב shadow: הוא קורא עותק מהאירועים, מחשב תוצאה ושומר השוואה בלי לבצע כתיבה חיצונית. כך ניתן להשוות בין פרשנות V1 ל-V2 על תרחישים מורשים לפני העברת הפעולה.
בהשוואה בודקים יותר מספירת הצלחות:
- האם אותן ישויות קיבלו אותו סטטוס כאשר המשמעות אמורה להישמר.
- אילו אירועים לא הומרו או קיבלו ערך לא מוכר.
- האם סדר האירועים נשמר לכל ישות.
- האם מספר הפעולות העסקיות הצפוי השתנה.
- האם יש פער שנובע משינוי מכוון או מתקלה.
בשלב המעבר צריך להיות נתיב כתיבה יחיד לכל פעולה. אין לתת לשני צרכנים לבצע עדכון רק כדי להשוות ביניהם. אפשר להשוות חישוב, אך side effects נשארים במסלול המורשה בלבד.
פריסה הדרגתית לפי סוג אירוע, קבוצת ישויות או consumer מאפשרת לעצור השפעה מצומצמת. תנאי עצירה כוללים גרסה לא מוכרת, עלייה באירועים שלא ניתנים להמרה, פער עסקי בלתי מוסבר או backlog שממשיך לגדול.
לשמור observability לפי גרסה
בלי מדדים לפי גרסת schema קשה לדעת האם המעבר הסתיים. לכל אירוע כדאי לשמור metadata שאינו רגיש ומאפשר לזהות לפחות סוג, גרסה, producer, זמן פרסום, Event ID ו-correlation ID.
במהלך המעבר עקבו אחרי:
- מספר אירועים שנקלטו מכל גרסה.
- מספר צרכנים פעילים והגרסאות שהם מעבדים.
- כשלי validation והמרה לפי סיבה.
- ערכי enum לא מוכרים ושדות חובה חסרים.
- אירועים שהועברו למסלול חריג או ל-DLQ.
- פערי shadow בין תוצאה ישנה לחדשה.
- גיל האירוע הישן ביותר מכל גרסה.
אין צורך לשמור payload מלא בלוג כדי לקבל תצפית. לעיתים מזהים, גרסה וסיבת כשל מספיקים ומקטינים חשיפה של מידע רגיש. קישור בין שירותים באמצעות correlation ID עוזר לאתר היכן הפרשנות השתנתה לאורך התהליך.
להסיר גרסה ישנה רק על בסיס הוכחות
לפני contract או הפסקת V1, מוודאים:
- כל הצרכנים הידועים עברו ואישרו את התוצאה העסקית.
- לא נצפתה צריכת V1 במשך חלון שמתאים גם לתהליכים נדירים.
- אין backlog, DLQ או outbox פעיל בגרסה הישנה.
- קיימת דרך לעבד היסטוריה אם מדיניות השמירה דורשת זאת.
- מדדי ההמרה והחריגים יציבים ואין פער לא מוסבר.
- תיעוד, דוגמאות ובדיקות עודכנו לגרסה הנתמכת.
- יש תוכנית עצירה וטיפול אם צרכן לא ידוע מתגלה לאחר ההסרה.
חזרה לאחור אינה תמיד פשוטה. אם צרכן חדש כבר כתב מצב לפי משמעות חדשה, החזרת הקוד בלבד אינה מבטלת את הכתיבות. צריך להפריד בין rollback של רכיב לבין reconciliation של הפעולות שכבר בוצעו.
שאלות נפוצות
האם הוספת שדה אופציונלי תמיד בטוחה?
לא. רוב הצרכנים הסובלניים יכולים להתעלם ממנו, אך מפענחים קשיחים, חתימות על payload מלא או לוגיקה שמעתיקה את כל המבנה עלולים להישבר. בודקים את מדיניות התאימות מול צרכנים אמיתיים.
האם schema registry פותר את ניהול הגרסאות?
הוא יכול לרכז schemas, לאכוף כללי תאימות ולעזור לזהות שימוש, אך הוא אינו מגדיר לבדו משמעות עסקית, בעלות, מסלול מעבר או טיפול באירועים היסטוריים. נדרש תהליך סביבו.
כמה גרסאות consumer צריך לתמוך?
אין מספר קבוע. התמיכה נקבעת לפי משך ההמתנה בתורים, מדיניות replay ושמירת ההיסטוריה. אפשר לתמוך ישירות בכמה גרסאות או להמיר אותן למודל קנוני, כל עוד הכללים בדוקים ומתועדים.
מה עושים כאשר צרכן לא מוכר מתגלה מאוחר?
עוצרים את הסרת התאימות אם עדיין אפשר, מזהים בעלים ובודקים באילו שדות ומשמעות הוא תלוי. אם הגרסה כבר הוסרה, לא מפעילים מחדש פרסום כפול ללא בדיקת כפילויות ו-side effects; מתכננים מתאם או מסלול מעבר מבוקר.
להתחיל מאירוע אחד ומפת צרכנים אחת
בחרו סוג אירוע אחד שעומד להשתנות. תעדו את המשמעות העסקית, רשמו את הצרכנים והגרסאות שלהם, סווגו את השינוי, ובנו contract tests שמכסים גם ערך לא מוכר ואירוע ישן. בצעו expand, השוו את התוצאה ללא side effects, העבירו צרכנים בהדרגה ורק אז הסירו את החוזה הקודם.
Adaptico מסייעת לארגונים לתכנן אינטגרציות, APIs ותהליכי אוטומציה בין מערכות קיימות. אפשר [ליצור קשר](/#contact) כדי למפות אירוע אחד, את הצרכנים שלו ואת מסלול הגרסאות לפני ששינוי schema מגיע לייצור.