יום שני, 12 בדצמבר 2011

על Domain Driven Design

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

הקדמה
הקשר בין גודל התוכנה לסיבוכיות שבה הוא קשר ברור וידוע.

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

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

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

הרבה יודעים לתת הנחיות וטיפים, אך מלבד בודדים (כמו תנועת האג'ייל שהציגה שיטות ישימות), הרוב סתם מדברים. פעם עבדתי בחברה בה הביאו מרצה אורח שדיבר על פשטות ובסופה חילקו לכל העובדים ספר משעמם עד גרוע על פשטות. המרצה סיפר בהתלהבות כמה פשטות היא טובה בכל תחומי החיים, אך מלבד העצה "לחשוב פשוט על פשטות" - הוא לא עזר בכלל להגיע לשם :(

ובכן, היכונו! אוונס וה DDD (כלומר [Domain Driven Design [1) הולכים ללמד אתכם כיצד לקבל דרישות איכותיות יותר וכיצד לבנות מערכת שתהיה גמישה יותר לקבלת דרישות עתידיות. הדרך להתמקצעות היא לא פשוטה, אך כל מי שיש לו גישה לדרישות של לקוחות וקצת חשיבה מופשטת יכול די מהר להגיע לתוצאות ראשונות.

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

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

על התהליך הנפוץ לתכנון תוכנה
לא משנה אם אתם עובדים Waterfall או Agile, קרוב לוודאי שהמתכנתים לא אוספים את הדרישות מהלקוח בעצמם. יתרה מכך, גם שמדברים עם הלקוח לא תמיד יהיה זה המומחה העסקי. ייתכן ואתם מדברים עם מנהל התפעול בבנק ("לקוח") שרוצה מערכת לניהול משכנתאות, אבל אתם לא מדברים עם מי שמתעסק עם זה בפועל (Domain Expert). על מנת לפשט לכם את הנושא, מנהל התפעול משתמש במושגים שיותר קלים להבנה. ה PM לוקח צעד אחד הלאה ומשתמש במושגים יותר מקובלים מהעולם-הלא-מקצועי כי "מפתחים יאבדו כיוון אם נציף אותם בכל המידע הזה". זהו הרי תפקידו.
הארכיטקט או המומחה הטכנולוגי מפשט את הדברים קצת יותר להתאים לתשתית הקיימת , זה תפקידו, והופ - המפתחים מבינים בקלות על מה מדובר וניגשים מייד לעבודה.

הנה דוגמא ויזואלית של הסיפור עבור מערכת מעקב חיובים. אמנם פיתחתי כזו לפני 10 שנים ויותר, ובוודאי שכחתי את הרוב. סליחה על אי-דיוקים או חוסר שלמות במודל. הדוגמא למטה אינה מבוססת על מספיק ידע בכדי לפתח מערכת חיובים ע"פ ה DDD, אבל אני מקווה שהיא מספיקה לצורך הדגמת התהליך (יש לקרוא משמאל לימין):
מקור: softwarearchiblog

כפי שסיפרנו ה Domain Expert (אשתמש ב DE מעכשיו) משתמש בטרמינולוגיה משלו וכל שאר הגורמים בדרך מתרגמים, שלב אחר שלב, כך שלמפתחים יהיה ברור מה "עליהם לעשות". הכל טוב, לזמן מה - עד אשר מגיע שינוי לא-צפוי בדרישות:

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

בואו נעצור לרגע: אנו מדברים במושגים ("חשבונית") ובתסריטים (Use-Cases) אך מאחורי כל אלה יש לנו מודל תפיסתי (Conceptual Model) שמתאר איך הדברים מתרחשים. בני-אדם, כמונו, נוטים להרגיש אי-נוחות בעת שהם עוסקים בנושא לא מוכר. כתרופה, מנגנוני החשיבה שלנו גורמים לנו להשלים, באופן כמעט מיידי, את סט העובדות שניתנו לנו למודל תפיסתי הגיוני (מבחינתנו). איכות העובדות שהועברה לנו, כשרון ומזל יכתיבו עד כמה נכון המודל שבנינו. לא מעט ריבים / חוסרי-הבנה נוצרים בגלל הפער שבין המודל של אדם א' לאדם ב', שניהם ניזונו מאותן עובדות.

הפתרון "הרשמי"
אז מה עושים? אחד העקרונות המרכזיים של DDD הוא שפה-רוחבית (ubiquitous language). על מישהו, ה business analyst, לחפור ללמוד ולהגדיר עם ה DEs שפה אחידה ומוסכמת על פיה כולם יעבדו, שפה שבעצם תגזר מתוך Conceptual Model ברור ומלוטש (במידת האפשר).

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

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

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

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

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

ע"פ DDD, יש להשקיע זמן לא דווקא בשמיעת עוד דרישות וציור עוד מסכים - דבר שאנו מתפתים לעשות, אלא בלמידה לעומק של הדומיין. הדומיין יתואר במודל (להלן Conceptual Model) שהשימוש בו יהיה חוצה גבולות: לקוחות, מחלקת מוצר/שיווק ומחלקות הפיתוח השונות. כל דרישה עתידית תעבור דרך אותו מודל ותעדכן אותו ע"פ הצורך, אך סביר שהשינויים יהיו קטנים והגיוניים.
אם הקוד יהיה שיקוף של המודל - גם השינויים בו יהיו קלים יותר.

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


מודל קונספטואלי המתאר פעולה בנקאית. האם המפתחים, מנהלי המוצר והלקוחות שותפים לאותה תמונה (או הבנה)? מקור: martinfowler.com


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

אילוסטרציה: domain experts.
כמעט ושמתי תמונה אומנותית של מנהל חשבונות זקן משנות ה-50, אך כנראה שבישראל בשנות ה-10, תתקלו יותר ב domain experts שדומים לאלו, שני מנהלים ב-888.
מקור: כתבה ב"אנשים ומחשבים".
בכל זאת יש כמה צעדים שאתם יכולים לעשות מתוך הפיתוח על מנת לשפר את המצב.

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

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

הקשיבו לשפה
כלל #1: אם בשיחה עם הלקוח (DE), הלקוח משתמש הרבה במושג (למשל, "חשבונית") ואין לכם אובייקט מרכזי בשם "חשבונית" - זהו סימן לפער במודל. תקנו את המודל שייתן משקל מתאים למושגים החשובים בהם משתמש הלקוח בשיחה היום-יומית.

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

זכרו: "(You can run, but you cannot hide (a bad conceptual model". אם "תשקרו" את המערכת ותעבדו עם מודל לא מדוייק, זה יחזור אליכם בדמות תחזוקה קשה ודרישות עתידיות הקשות ליישום.
לדוגמא: תיאור של מחשב עם 2 כתובות IP כשני מחשבים עם אותו השם, כל אחד עם כתובת IP אחת - אולי יקצר את הפיתוח בשלב הראשון, אך יחזור אליכם כמוברנג כחלק מדרישות עתידיות. ניסיתם "לשקר" במודל.

סיכום
למי שהתנסה בהצלחה במידול בכלל או DDD בפרט, מידול נראה חלק הכרחי בבנייה של כל מערכת. תורת המידול אינה נפוצה ואינה קלה - אך היא מביאה תוצאות ממשיות. המקור הטוב ביותר שאני נתקלתי בו עד היום הוא הספר Analysis Patterns של Martin Fowler, ספר קצת לא-מסודר אך בעל תובנות עמוקות.

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

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

[1] בעברית: תכנון מונחה-תחום, תרגום לא כל-כך מוצלח לדעתי - אך לא מצאתי טוב יותר.

תגובה 1:

  1. הספר הראשון של Fowler שאיזכרת - Analysis Patterns - פשוט מעולה, ומומלץ בחום. אל תשפטו ספר ע"פ שנת ההוצאה...

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

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

    השבמחק