מה זה?

בשיעור הקודם דיברנו על “קווי תפר” עם Coupling נמוך — אבל איך בדיוק מזהים אותם? חיתוך לפי טבלת DB (“שירות לכל טבלה”) או שכבה טכנית (“שירות ל-Controllers, שירות ל-Services”) כמעט תמיד שגוי — הוא מפרק לפי מבנה טכני, לא לפי משמעות עסקית. Bounded Context (מ-DDD — Domain-Driven Design) פותר את זה: מפרקים לפי יכולת עסקית שלמה ועצמאית — “הזמנות”, “מלאי”, “משתמשים” — לא לפי טבלה או שכבת קוד.

מילות מפתח שחשוב לזכור

• DDD (Domain-Driven Design) — גישת תכנון שממקדת את מבנה הקוד סביב הדומיין העסקי, לא סביב מבנה טכני

• Bounded Context — גבול ברור סביב מודל עסקי שלם ועצמאי — בתוכו, מונחים ומודלים עקביים; מחוצה לו, אין הנחות

• Ubiquitous Language (שפה משותפת) — מונחים עסקיים זהים בין קוד, תיעוד ושיחה עם אנשי עסקים — לא “תרגום” בין השפות

• Same Word, Different Meaning — אותה מילה (“משתמש”) יכולה להיות ישות שונה לגמרי בשני Bounded Contexts שונים

  flowchart TB
    subgraph BC1["Bounded Context: Orders"]
        O["Order"] --> OI["OrderItem"]
        O --> Cust1["Customer<br/>(only: name, shipping address)"]
    end

    subgraph BC2["Bounded Context: Billing"]
        Inv["Invoice"] --> Cust2["Customer<br/>(only: payment details, outstanding balance)"]
    end

    BC1 -.->|"communication (Kafka/HTTP)"| BC2

הסבר עיקרי

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

חיתוך לפי DB/שכבה טכנית מפספס את הגבול האמיתי — “שירות למשתמשים, שירות למוצרים, שירות להזמנות” (לפי טבלאות) נשמע הגיוני, אבל בפועל יוצר שירותים שכל הזמן “צריכים” מידע אחד מהשני (הזמנה צריכה גם משתמש וגם מוצר) — Coupling גבוה דרך הרשת, גרוע יותר מ-Coupling בתוך אותו תהליך. Bounded Context לפי יכולת עסקית (“ניהול הזמנות” כולל את כל מה שצריך כדי לבצע הזמנה, כולל עותק מצומצם ורלוונטי של פרטי לקוח) מייצר שירותים שבאמת עצמאיים.

Ubiquitous Language מונע “תרגום” מיותר — כשאנשי עסקים אומרים “הזמנה” והקוד קורא לזה Order (לא PurchaseTransaction או שם טכני אחר), אין צורך “לתרגם” בין שיחה לקוד — המונחים זהים. זה מקטין אי-הבנות בין דרישות עסקיות למימוש טכני.

יתרונות

Bounded Context מפרק לפי משמעות עסקית אמיתית, לא מבנה טכני שרירותי; כל Context עצמאי עם המידע הרלוונטי לו בלבד — Coupling נמוך יותר בין שירותים; Ubiquitous Language מקטין פערי תקשורת בין צוותי פיתוח לבעלי עניין עסקיים.

חסרונות

זיהוי גבולות Context נכונים דורש הבנה עסקית עמוקה, לא רק ידע טכני; אותה ישות (“לקוח”) מיוצגת בכמה מקומות עם מידע חלקי שונה — דורש תיאום כשמשהו משתנה בשני הצדדים.

נקודות חשובות

• Bounded Context = גבול סביב יכולת עסקית שלמה, לא סביב טבלת DB או שכבה טכנית

• אותה מילה עסקית (“Customer”) יכולה לייצג ישויות שונות בקונטקסטים שונים, בכוונה

• Ubiquitous Language = מונחי הקוד זהים למונחי אנשי העסק, לא “מתורגמים”

• חיתוך לפי DB/שכבה טכנית נוטה ליצור Coupling גבוה מדי דרך הרשת בין שירותים

טעויות נפוצות

• לחתוך שירותים לפי טבלת DB (“שירות לכל טבלה”) במקום לפי יכולת עסקית

• לנסות לבנות מודל “Customer” אוניברסלי אחד שמשרת את כל הקונטקסטים — הופך מנופח וקשה לתחזוקה

• להתעלם מ-Ubiquitous Language ולתת לקוד להשתמש בשמות טכניים שונים ממה שאנשי העסק קוראים לדברים

סיכום

Bounded Context (מתוך DDD) מפרק מערכת לפי יכולת עסקית שלמה ועצמאית, לא לפי טבלת DB או שכבה טכנית. אותה ישות עסקית (כמו “לקוח”) יכולה להיות מיוצגת אחרת בכל קונטקסט, עם רק המידע הרלוונטי לו. Ubiquitous Language מבטיח שהקוד משתמש באותם מונחים כמו השיחה העסקית עצמה. זהו הכלי המרכזי לזיהוי “קווי התפר” הנכונים לפירוק מיקרוסרביסים — לא ניחוש טכני, אלא הבנה עסקית ממוקדת.

דוקומנטציה רשמית

Martin Fowler — Bounded Context


תרגילים

תרגיל 1 — זיהוי מודלים שונים לאותה ישות

המשימה: למערכת מסחר אלקטרוני, תארו איך “Product” (מוצר) ייראה אחרת בשני Bounded Contexts: “קטלוג” (תיאור, תמונות, מחיר) ו"מלאי" (כמות במחסן, מיקום).

בדיקה: שני המודלים שונים בבירור בשדות, כל אחד רלוונטי רק לקונטקסט שלו.

תרגיל 2 — חיתוך שגוי מול נכון

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

בדיקה: ההסבר מתייחס במפורש לרמת Coupling בין השירותים בכל גישה.


פרויקט מסכם

המשימה: תכננו Bounded Contexts למערכת “מודיעין עסקי” (הכנה לפרויקט הצוותי בהמשך הקורס) עם שירותי Collector, Analyzer, Alert, Dashboard.

דרישות:

  1. הגדירו לכל שירות את גבול האחריות העסקי שלו במשפט אחד ברור
  2. זהו איזה מידע כל שירות חייב לקבל מהשירותים האחרים (ולא לייצר בעצמו)
  3. הציגו לפחות מקרה אחד שבו אותו מונח עסקי (“אירוע”, “התראה”) משמעותו שונה בשני שירותים

בדיקה: התכנון מראה גבולות ברורים בין השירותים, בלי חפיפה מיותרת באחריות.

מה בפרק הבא

בפרק הבא — תקשורת בין שירותים: sync HTTP/gRPC מול async Kafka, ו-API Gateway/Service Discovery — איך שירותים עצמאיים בכל זאת מוצאים ומדברים זה עם זה.