מה זה Domain Driven Design¶
בפוסט על מעבר ממונוליט למיקרוסרביסים דיברנו על "גבול עסקי ברור" כתנאי לפיצול נכון של מערכת. הרעיון הזה לא הומצא סתם - הוא חלק ממתודולוגיה שלמה שנקראת Domain Driven Design, או בקיצור DDD, ושווה להכיר אותה גם אם אתם לא מתכננים לפצל שום דבר בקרוב.
הבעיה שה-DDD בא לפתור¶
הרבה מערכות תוכנה מתחילות ישר מהמודל הטכני - טבלאות, API endpoints, מבני נתונים - בלי לעצור ולהבין לעומק את התחום העסקי שהמערכת אמורה לשרת. התוצאה היא קוד שמשקף איך המתכנת חשב על הבעיה, לא איך אנשי העסק באמת חושבים עליה. וכשהמונחים בקוד לא תואמים למונחים שאנשי העסק משתמשים בהם, כל שיחה בין הצדדים הופכת לתרגום מתיש, ובאגים נובעים מאי הבנות שנשארות סמויות עד שמישהו מגלה אותן בייצור.
Domain Driven Design, גישה שגובשה על ידי אריק אוונס בתחילת שנות האלפיים, מציעה לבנות את המודל בקוד כך שהוא ישקף במדויק את התחום העסקי, לא רק את הצרכים הטכניים.
שפה משותפת - ubiquitous language¶
העיקרון הראשון והבסיסי ביותר הוא ליצור שפה משותפת בין מפתחים לאנשי העסק, ולהשתמש באותם מונחים בדיוק בשני הצדדים - גם בשיחות, גם במסמכים, וגם בשמות המחלקות והמתודות בקוד. אם אנשי העסק מדברים על "הזמנה שממתינה לאישור מלאי", המחלקה בקוד צריכה להיקרא בדיוק כך, לא Order עם דגל בוליאני סתום בשם status_2.
זה נשמע כמו פרט קטן, אבל בפועל זה משנה איך דנים בבעיות. כשמפתח ואיש עסקים משתמשים באותה מילה בדיוק לאותו דבר, קל הרבה יותר לגלות אי הבנות מוקדם, לפני שהן הופכות לבאג בייצור.
Bounded Context - הגבול שבו המונחים תקפים¶
מערכות גדולות מתמודדות עם בעיה מעניינת - אותה מילה יכולה לשמש בשני מובנים שונים לגמרי בחלקים שונים של אותו עסק. "לקוח" בהקשר של מכירות הוא מישהו עם פוטנציאל לקנות, עם שדות כמו תחומי עניין והיסטוריית פניות. "לקוח" בהקשר של חיוב הוא ישות עם כתובת לחיוב, אמצעי תשלום, והיסטוריית חשבוניות. זו לא אותה ישות, גם אם השם זהה.
Bounded Context הוא הגבול שבתוכו מונח מסוים משמעותי בצורה עקבית. מחוץ לגבול, אותו מונח יכול להתפרש אחרת לגמרי, וזה בסדר - כי כל context מתמקד רק במה שרלוונטי לו. הרעיון הזה הוא בדיוק מה שמאפשר לזהות גבולות טבעיים בין מודולים או שירותים - כל bounded context הוא מועמד טבעי להיות מודול נפרד במונוליט, או שירות נפרד במיקרוסרביסים.
אגרגטים - הגבול של עקביות¶
מושג מרכזי נוסף הוא aggregate - קבוצת אובייקטים שצריכה להישאר עקבית ביחד כיחידה אחת. למשל, הזמנה (Order) ופריטי ההזמנה שלה (OrderItems) הם אגרגט אחד - אי אפשר לשמור הזמנה עם סכום כולל שלא תואם את סכום הפריטים בפועל. יש להם "שורש" (aggregate root), במקרה הזה Order, שדרכו בלבד מותר לגשת ולשנות את שאר האובייקטים באגרגט. אף חלק חיצוני לא נוגע ישירות ב-OrderItems, רק דרך Order שאוכפת את כללי העקביות.
זה עוזר לענות על שאלה מעשית מאוד - כשמעצבים טרנזקציה במסד נתונים, איפה הגבול שלה צריך לעבור? התשובה הרווחת ב-DDD היא - טרנזקציה אחת לאגרגט אחד. אם אתם מוצאים את עצמכם צריכים טרנזקציה שחוצה כמה אגרגטים בבת אחת כל הזמן, זה סימן שאולי הגדרתם את גבולות האגרגט לא נכון.
DDD אסטרטגי מול טקטי¶
חשוב להבחין בין שתי רמות ב-DDD. הרמה האסטרטגית עוסקת בגבולות גדולים - זיהוי bounded contexts וכיצד הם מתקשרים, וזו בדיוק העבודה שמקדימה כל החלטה על פיצול מונוליט לשירותים. הרמה הטקטית עוסקת בבניית הקוד בתוך context בודד - אגרגטים, entities, value objects, ואירועי domain. אפשר וכדאי להשתמש ברמה האסטרטגית גם בלי לאמץ את כל הכלים הטקטיים, אם רק רוצים לצייר גבולות נכונים במערכת קיימת.
מתי זה משתלם¶
DDD דורש השקעה אמיתית - ישיבות עם אנשי עסק, בניית מודל מדויק, ולפעמים שינוי שמות בקוד קיים כדי שיתאימו לשפה המשותפת. זה משתלם בעיקר בתחומים עסקיים מורכבים באמת, עם הרבה כללים עדינים וחריגים. במערכת CRUD פשוטה בלי לוגיקה עסקית מסובכת, כל המנגנון הזה הוא כנראה תקורה מיותרת.
לסיכום¶
Domain Driven Design מציע לבנות את מודל הקוד כך שישקף במדויק את התחום העסקי, באמצעות שפה משותפת בין מפתחים לאנשי עסק, גבולות ברורים (bounded contexts) בין תחומים שונים, ואגרגטים שאוכפים עקביות. הערך האמיתי שלו מתגלה בתחומים עסקיים מורכבים, ובדיוק שם הוא גם מספק את הבסיס לזיהוי גבולות נכונים בין מודולים או שירותים במערכת גדולה.
בקורס ארכיטקטורת תוכנה שלנו אנחנו עוברים על DDD לעומק, כולל תרגיל מלא של זיהוי bounded contexts במערכת אמיתית.
מתלבטים איך לצייר גבולות נכונים במערכת שלכם? הצטרפו לקהילה בדיסקורד ותביאו את המקרה שלכם לדיון.