עיצוב מונחה דומיין (DDD) במיקרו-שירותים
כאשר ארגונים עוברים מארכיטקטורה מונוליטית לשירותי מיקרו, הם מתמודדים עם שאלה קריטית וחשובה: כיצד נשרטט את הגבולות של השירותים שלנו?
בתיאוריה, שירותי מיקרו צריכים להיות יחידות רופפות, מנותקות, שניתן לפתח, לפרוס ולהגדיל באופן עצמאי. אולם בפועל, בסופו של דבר צוותים רבים בונים מונוליט מבוזר - מערכת שבה השירותים מחוברים בצורה הדוקה כל כך עד ששינוי עסקי בודד מצריך שינוי ופריסה של מספר שירותים בו-זמנית, מה שמוסיף את זמן השהיית הרשת והפריסה.
כדי להימנע ממלכודת זו, אדריכלי תוכנה פונים אל עיצוב מונחה תחום (DDD). DDD, שהוצג לראשונה על ידי אריק אוונס בשנת 2003, היא מתודולוגיה לפיתוח תוכנה המיישרת מבני קוד עם התחומים העסקיים המורכבים שהם מייצגים.
במאמר זה, נחקור כיצד DDD מספק את השרטוטים האסטרטגיים והטקטיים לתכנון מיקרו-שירותים נקיים, מנותקים וניתנים לתחזוקה גבוהה.
1. התוכנית האסטרטגית: שרטוט גבולות השירות
DDD מחולק לשני שלבים עיקריים: עיצוב אסטרטגי ועיצוב טקטי. עיצוב אסטרטגי עוסק ביצירת מודלים של הקשרים עסקיים והגדרת גבולות שירות. הוא מספק את תצוגת ה"מאקרו" של הארכיטקטורה.
א. שפה בכל מקום
בתוך עסק, מחלקות שונות משתמשות באותן מילים כדי להתכוון לדברים שונים לחלוטין. לדוגמה:
- עבור צוות המכירות, “משתמש” הוא ליד או לקוח פוטנציאלי.
- עבור צוות האבטחה, “משתמש” הוא קבוצה של אישורי כניסה.
- עבור צוות המשלוח, “משתמש” הוא כתובת נמען פיזית.
הניסיון לבנות מודל מסד נתונים אחד ומאוחד עבור “משתמש” שמספק את כולם מביא לבסיס קוד עצום וסבוך. DDD פותר זאת על ידי הקמת שפה בכל מקום - אוצר מילים משותף המוגדר ומשמש גם מומחי תחום עסקי וגם מפתחים בתוך גבול מסוים.
ב. הקשרים מוגבלים
הקשר מוגבל הוא הגבול המפורש שבתוכו חל מודל תחום. בתוך הגבול, לכל המונחים בשפה בכל מקום יש משמעות אחת וחד משמעית.
במקום מודל “משתמש” יחיד, אנו מגדירים מודלים נפרדים בתוך ההקשרים הגבולות המתאימים שלהם:
- בקונטקסט ההזמנה, יש לנו מודל
Orderהמכיל מידע ליצירת קשר עם הלקוח. - בהקשר הזהות, יש לנו מודל
Credentials. - בהקשר למשלוח, יש לנו מודל
DeliveryAddress.
כלל הזהב של שירותי מיקרו: הקשר אחד מוגבל ממפה ישירות לשירות מיקרו אחד.
על ידי חלוקת המערכת ל-Bounded Contexts, אנו מבטיחים שהמיקרו-שירותים מחוברים בצורה רופפת ומייצגים יכולות עסקיות מובחנות.
ג. מיפוי הקשר: כיצד שירותים מתקשרים
הקשרים מוגבלים אינם קיימים בנפרד; הם חייבים לקיים אינטראקציה. מפת הקשר מגדירה את היחסים ומנגנוני התרגום בין הקשרים. דפוסי מפתח כוללים:
- ** ליבה משותפת**: שני הקשרים חולקים תת-קבוצה קטנה של מודל הדומיין ומסד הנתונים (בדרך כלל מונעים משירותי מיקרו עקב צימוד).
- לקוח-ספק: הקשר אחד (ספק) חייב לספק נתונים לאחר (לקוח). על הספק לתאם עם הלקוח לוחות זמנים לשחרור.
- שכבת אנטי-שחיתות (ACL): שכבת תרגום המתרגמת נתונים נכנסים ממערכת חיצונית למודל התחום הפנימי של הלקוח, ומונעת ממודלים חיצוניים לזהם את הארכיטקטורה הפנימית.
2. התוכנית הטקטית: בניית בסיס הקוד של שירות המיקרו-שירותים
ברגע שגבולות השירות נשרטטו באמצעות עיצוב אסטרטגי, עיצוב טקטי מספק קבוצה של דפוסי עיצוב למבנה הקוד בתוך שירות מיקרו יחיד.
א. ישויות וחפצי ערך
בתוך שירות מיקרו, המודלים מחולקים לשתי קטגוריות:
- ישויות: אובייקטים בעלי זהות ייחודית הנמשכת לאורך זמן, גם אם התכונות שלהם משתנות. דוגמאות כוללות
OrderאוProduct. - חפצי ערך: אובייקטים שאין להם זהות ייחודית ומוגדרים לחלוטין לפי התכונות שלהם. הם בלתי ניתנים לשינוי. דוגמאות כוללות
ShippingAddressאוProductDimensions. אם לשני אובייקטים ערכיים יש אותן תכונות, הם נחשבים שווים.
ב. אגרגטים ומצטברים שורשים
מצבר הוא מקבץ של ישויות ואובייקטי ערך משויכים המטופלים כיחידה אחת לשינויי נתונים. לכל אגרגט יש Aggregate Root, שהיא נקודת הכניסה היחידה שדרכה אובייקטים חיצוניים יכולים לקיים אינטראקציה עם המצטבר. השורש מבטיח שכל האיוריאנטים העסקיים (הכללים) נאכפים.
לדוגמה, בתרשים למעלה:
- בשירות ההזמנות, ה-
Orderהוא השורש המצטבר. הוא מכילOrderItem(ישות) וShippingAddress(אובייקט ערך). שירותים חיצוניים אינם יכולים לשנות אתOrderItemישירות; עליהם לקרוא לשיטה בשורשOrder(לדוגמה,order.AddItem()), אשר מאמתת שההזמנה לא נשלחה כבר.
ג. אירועי דומיין
אירוע דומיין הוא משהו שקרה בתחום שלמומחים עסקיים אכפת ממנו. הוא משמש לתקשורת שינויים על פני הקשרים מוגבלים באופן אסינכרוני.
כאשר Aggregate מבצע פקודה, הוא מפרסם אירוע דומיין (למשל, OrderCreated). שירותים אחרים מקשיבים לאירוע זה ומעדכנים את מצבם בהתאם, ומבטיחים עקביות בסופו של דבר ללא תלות סינכרונית של API.
3. דוגמה קונקרטית: שירות הזמנות לעומת שירות מלאי
הבה נסתכל על יישום פשוט של DDD טקטי ב-Go for the שירות ההזמנות, מראה כיצד השורש המצטבר מתאם כללים עסקיים.
package domain
import (
"errors"
"time"
)
// Value Object (Immutable, identity-less)
type ShippingAddress struct {
Street string
City string
ZipCode string
}
// Entity (Has identity, mutable)
type OrderItem struct {
ProductID string
Quantity int
Price float64
}
// Aggregate Root (Enforces transactional boundaries)
type Order struct {
ID string
Items []OrderItem
Address ShippingAddress
Status string
CreatedAt time.Time
}
// NewOrder creates a new Order Aggregate Root
func NewOrder(id string, address ShippingAddress) *Order {
return &Order{
ID: id,
Items: []OrderItem{},
Address: address,
Status: "PENDING",
CreatedAt: time.Now(),
}
}
// AddItem enforces business rules before mutating state
func (o *Order) AddItem(productID string, qty int, price float64) error {
if o.Status != "PENDING" {
return errors.New("cannot add items to a finalized or cancelled order")
}
if qty <= 0 {
return errors.New("quantity must be greater than zero")
}
o.Items = append(o.Items, OrderItem{
ProductID: productID,
Quantity: qty,
Price: price,
})
return nil
}
כאשר ההזמנה נשמרת בהצלחה, השירות מפרסם אירוע למתווך הודעות:
{
"event_id": "evt_98231",
"event_type": "OrderCreated",
"timestamp": "2026-06-26T00:15:00Z",
"payload": {
"order_id": "ord_5521",
"items": [
{ "product_id": "prod_88", "quantity": 2 }
]
}
}
שירות המלאי מאזין לאירוע הזה, מעדכן את הישות המקומית StockLevel שלו ומשלים את הזרימה באופן אסינכרוני.
4. DDD אסטרטגי לעומת טקטי בשירותי מיקרו
| שלב / היבט | DDD אסטרטגי | DDD טקטי |
|---|---|---|
| היקף | ארכיטקטורת מערכת גלובלית (מאקרו) | בתוך בסיס קוד שירות יחיד (מיקרו) |
| מטרה ראשית | צייר גבולות שירות נקיים ומנותקים | מודל לוגיקה עסקית עשירה ואכיפה אינוריאנטים |
| מושגי מפתח | הקשרים מוגבלים, שפה בכל מקום, מפת הקשר | ישויות, אובייקטי ערך, אגרגטים, אירועי דומיין |
| קהל | אדריכלים, מנהלי מוצר, מפתחים | מפתחי תוכנה, סוקרי קוד |
| השפעה על שירותי מיקרו | קובע את מספר והיקף השירותים | קובע תנועות מסד נתונים ומבנה ספריות |
מסקנה: התחל קודם עם DDD אסטרטגי
החלת עיצוב מונחה דומיין על שירותי מיקרו היא דרך רבת עוצמה להבטיח שהארכיטקטורה שלך תואמת את המבנה העסקי שלך. עם זאת, צוותים עושים לעתים קרובות את הטעות שהם מתמקדים יותר מדי בדפוסים טקטיים (כמו כתיבת ממשקי מאגר ואובייקטים ערכיים) תוך התעלמות מעיצוב אסטרטגי.
אם לא תבין נכון את הגבולות, שום כמות של קוד טקטי נקי לא תציל את המערכת שלך מלהפוך למונוליט מבוזר. התחל תמיד במיפוי אסטרטגי - הגדירו את השפה בכל מקום, קבצו את ההיגיון להקשרים מוגבלים, מפו את זרימות התקשורת ותנו לגבולות המיקרו-שירותים שלכם לצוץ באופן טבעי מתוך ההגדרות הללו.
גלה עוד ארכיטקטורת תוכנה, דפוסי עיצוב ותובנות הנדסיות בבלוג Ghaznix →