לדלג לתוכן

ארכיטקטורה מונעת אירועים - מדריך למתחילים

תארו לעצמכם שירות הזמנות שכשההזמנה מאושרת, צריך לשלוח מייל אישור, לעדכן מלאי, לחייב כרטיס אשראי, ולדווח למערכת אנליטיקס. הגישה הנאיבית היא ששירות ההזמנות יקרא ישירות לכל אחד מארבעת השירותים האלה, בזה אחר זה. זה עובד, אבל זה שביר מאוד. וזה בדיוק המקום שבו ארכיטקטורה מונעת אירועים נכנסת לתמונה.

הבעיה עם קריאות ישירות

בגישת הקריאות הישירות, שירות ההזמנות צריך לדעת על קיומם של כל ארבעת השירותים האחרים, ולהחזיק קוד שקורא לכל אחד מהם. אם שירות המייל איטי, שירות ההזמנות מחכה לו. אם שירות האנליטיקס נופל, כנראה שכל תהליך ההזמנה נכשל, למרות שהזמנה שלא דווחה לאנליטיקס זה בכלל לא אסון. וכל פעם שרוצים להוסיף עוד פעולה - למשל להוסיף נקודות מועדון - צריך לחזור ולשנות את קוד שירות ההזמנות עצמו.

זה בדיוק ההפך מעיקרון הפתיחות והסגירות שדיברנו עליו כשמדברים על SOLID - הקוד סגור לשינוי במקום פתוח להרחבה.

איך ארכיטקטורה מונעת אירועים פותרת את זה

הרעיון המרכזי - במקום ששירות אחד יקרא ישירות לכל השירותים שתלויים בו, הוא פשוט מפרסם אירוע - "הזמנה אושרה" - ולא אכפת לו מי מקשיב לאירוע הזה או מה עושים איתו. כל שירות שמתעניין - שירות המייל, שירות המלאי, שירות האנליטיקס - נרשם להאזין לאירוע הזה, ומטפל בו בקצב שלו.

שירות ההזמנות לא יודע כמה מאזינים יש, ולא יודע מה כל אחד עושה עם האירוע. הוא פשוט אומר "זה קרה", ופונה הלאה. אם מחר רוצים להוסיף עוד מאזין - שירות חדש שנותן נקודות מועדון - פשוט מוסיפים מאזין חדש, בלי לגעת בכלל בשירות ההזמנות.

התפקיד של מתווכי הודעות

זה לא קורה בקסם - צריך רכיב טכני שמעביר את האירועים בין המפרסם למאזינים. זה התפקיד של מתווכי הודעות (message brokers) כמו Kafka או RabbitMQ. השירות ששולח את האירוע פשוט מפרסם אותו לתוך המתווך, והמתווך דואג להעביר אותו לכל מי שנרשם להאזין.

המתווך גם פותר בעיה טכנית חשובה - מה קורה אם המאזין זמנית לא זמין? במקום שהאירוע פשוט יאבד, המתווך שומר אותו בתור עד שהמאזין חוזר לפעול ומעבד אותו. זה נותן למערכת עמידות שקשה מאוד להשיג עם קריאות ישירות בין שירותים.

היתרונות האמיתיים

  • ניתוק (decoupling). שירותים לא צריכים לדעת אחד על השני, רק על צורת האירוע. אפשר להוסיף ולהסיר מאזינים בלי לגעת בשירות שמפרסם.
  • עמידות בפני עומס. אם יש גל פתאומי של הזמנות, שירות ההזמנות רק צריך לפרסם אירועים מהר. השירותים שמאזינים יכולים לעבד את התור בקצב שלהם, בלי להיחנק.
  • קלות בהוספת פונקציונליות. תכונה חדשה שצריכה להגיב לאירוע קיים היא לרוב שירות חדש קטן שמאזין, בלי לגעת בקוד קיים כלל.

והמחיר שחשוב לדעת עליו

כמו עם כל תבנית ארכיטקטונית, יש מחיר אמיתי, וחשוב להכיר אותו לפני שקופצים על הכלי הזה:

  • עקביות סופית. כשההזמנה מאושרת, המייל לא נשלח באותו רגע בדיוק - הוא יישלח כשהמאזין יגיע אליו, שניות אחר כך. במקרים מסוימים זה בעיה אמיתית, במקרים אחרים זה לגמרי בסדר.
  • דיבוג הופך מורכב יותר. במקום לעקוב אחרי קריאת פונקציה אחת פשוטה, צריך לעקוב אחרי אירוע שעובר דרך מתווך ומטופל אסינכרונית במקום אחר לגמרי בקוד, לפעמים אפילו בשירות אחר. זה דורש תשתית ניטור ומעקב טובה.
  • תשתית נוספת לתחזק. מתווך הודעות הוא עוד רכיב שצריך להפעיל, לנטר, ולוודא שהוא זמין - הוא הופך לחלק קריטי מהמערכת.

מתי זה נכון ומתי זה מוגזם

לא כל אינטראקציה בין שני שירותים צריכה להפוך לאירוע. כשמשתמש מחכה לתשובה מיידית - למשל שאילתת חיפוש - קריאה ישירה עדיין הגיונית יותר. ארכיטקטורה מונעת אירועים מתאימה בעיקר לתהליכים שיכולים לקרות ברקע, ולמקרים שבהם כמה חלקים של המערכת צריכים להגיב לאותו אירוע בלי תלות אחד בשני.

הנושא הזה מתחבר ישירות גם ל-CQRS ולעיבוד אירועים מתקדם יותר, שאנחנו מפרקים לגורמים בקורס ארכיטקטורת תוכנה עם דוגמאות מלאות של מתי כל תבנית משתלמת.

מתלבטים אם המערכת שלכם צריכה תורי הודעות או שזה עדיין מוקדם מדי? שאלו בדיסקורד, יש שם אנשים שעברו בדיוק את ההחלטה הזאת.

לסיכום

ארכיטקטורה מונעת אירועים הופכת שירותים תלויים זה בזה לשירותים עצמאיים שמגיבים לדברים שקרו, במקום לקרוא ישירות אחד לשני. זה נותן גמישות ועמידות אמיתית, אבל בתמורה למחיר של עקביות סופית ותשתית מורכבת יותר. כמו כל החלטה ארכיטקטונית, השאלה היא לא אם זה "מודרני", אלא אם הבעיה שלכם באמת דורשת את הגמישות הזאת.