طراحی دامنه محور (DDD) در میکروسرویس ها
هنگامی که سازمانها از معماری یکپارچه به ریزخدمات تغییر میکنند، با یک سوال مهم و پرمخاطب مواجه میشوند: چگونه مرزهای خدمات خود را ترسیم کنیم؟
در تئوری، ریزسرویسها باید واحدهای شل و جداشده باشند که میتوانند به طور مستقل توسعه، گسترش و مقیاسبندی شوند. با این حال، در عمل، بسیاری از تیمها در نهایت به ساخت یکپارچه توزیعشده میپردازند – سیستمی که در آن سرویسها به قدری به هم پیوستهاند که یک تغییر تجاری نیازمند اصلاح و استقرار چندین سرویس به طور همزمان، ترکیب تاخیر شبکه و گریدلاکهای استقرار است.
برای اجتناب از این دام، معماران نرم افزار به طراحی دامنه محور (DDD) روی می آورند. اولین بار توسط اریک ایوانز در سال 2003 معرفی شد، DDD یک متدولوژی توسعه نرم افزار است که ساختارهای کد را با حوزه های تجاری پیچیده ای که آنها نمایندگی می کنند، تراز می کند.
در این مقاله، بررسی خواهیم کرد که چگونه DDD نقشه های استراتژیک و تاکتیکی را برای طراحی میکروسرویس های تمیز، جدا شده و بسیار قابل نگهداری ارائه می دهد.
1. طرح استراتژیک: ترسیم مرزهای خدمات
DDD به دو مرحله اصلی تقسیم می شود: طراحی استراتژیک و طراحی تاکتیکی. طراحی استراتژیک در مورد مدل سازی زمینه های کسب و کار و تعیین مرزهای خدمات است. نمای “کلان” معماری را ارائه می دهد.
الف. زبان همه جا حاضر
در یک کسبوکار، بخشهای مختلف از کلمات مشابهی برای معانی کاملاً متفاوت استفاده میکنند. به عنوان مثال:
- از نظر تیم فروش، “کاربر” یک مشتری پیشرو یا یک مشتری بالقوه است.
- برای تیم امنیتی **، “کاربر” مجموعه ای از اعتبارنامه های ورود است.
- برای تیم حمل و نقل، “کاربر” یک آدرس فیزیکی گیرنده است.
تلاش برای ایجاد یک مدل پایگاه داده واحد و یکپارچه برای یک “کاربر” که همه را راضی کند، منجر به یک پایگاه کد عظیم و درهم می شود. DDD این مشکل را با ایجاد زبان فراگیر حل میکند - واژگان مشترکی که هم متخصصان حوزه کسبوکار و هم توسعهدهندگان در یک مرز خاص تعریف و استفاده میکنند.
ب. زمینه های محدود
زمینه محدود مرز صریحی است که یک مدل دامنه در آن اعمال می شود. در داخل مرز، همه اصطلاحات در زبان همه جا یک معنای واحد و بدون ابهام دارند.
به جای یک مدل «کاربر» واحد، مدلهای جداگانه را در زمینههای محدود مربوطه آنها تعریف میکنیم:
- در زمینه سفارش، یک مدل
Orderحاوی اطلاعات تماس مشتری داریم. - در قسمت Identity Context، یک مدل
Credentialsداریم. - در زمینه حمل و نقل، ما یک مدل
DeliveryAddressداریم.
قانون طلایی Microservices: One Bounded Context مستقیماً به یک Microservice نگاشت می شود.
با تقسیم کردن سیستم به زمینههای محدود، اطمینان حاصل میکنیم که ریزسرویسها بهطور آزاد با هم جفت شدهاند و قابلیتهای تجاری متمایز را نشان میدهند.
ج. نقشهبرداری زمینه: خدمات چگونه ارتباط برقرار میکنند
زمینه های محدود به صورت مجزا وجود ندارند. آنها باید تعامل داشته باشند. نقشه زمینه روابط و مکانیسم های ترجمه بین زمینه ها را تعریف می کند. الگوهای کلیدی عبارتند از:
- هسته مشترک: دو زمینه زیرمجموعه کوچکی از مدل دامنه و پایگاه داده را به اشتراک می گذارند (معمولاً در میکروسرویس ها به دلیل جفت شدن توصیه نمی شود).
- مشتری-تامین کننده: یک زمینه (تامین کننده) باید داده ها را در اختیار دیگری (مشتری) قرار دهد. تامین کننده باید برنامه های عرضه را با مشتری هماهنگ کند.
- لایه ضد فساد (ACL): یک لایه ترجمه که داده های دریافتی از یک سیستم خارجی را به مدل دامنه داخلی مشتری تبدیل می کند و از آلودگی مدل های خارجی به معماری داخلی جلوگیری می کند.
2. طرح تاکتیکی: ساختار پایگاه کد میکروسرویس
هنگامی که مرزهای سرویس با استفاده از طراحی استراتژیک ترسیم می شوند، طراحی تاکتیکی مجموعه ای از الگوهای طراحی را برای ساختار کد در یک میکروسرویس ارائه می دهد.
الف. نهادها و اشیاء ارزش
در داخل یک میکروسرویس، مدل ها به دو دسته تقسیم می شوند:
- موجودات: اشیایی که هویت منحصر به فردی دارند که در طول زمان باقی می ماند، حتی اگر ویژگی های آنها تغییر کند. به عنوان مثال می توان به
OrderیاProductاشاره کرد. - اشیاء ارزش: اشیایی که هویت منحصر به فرد ندارند و کاملاً با ویژگی هایشان تعریف می شوند. تغییر ناپذیرند. به عنوان مثال می توان به
ShippingAddressیاProductDimensionsاشاره کرد. اگر دو شیء ارزش دارای صفات یکسان باشند، برابر در نظر گرفته می شوند.
ب. مصالح و ریشه های کل
جمع مجموعه ای از موجودیت ها و اشیاء ارزش مرتبط است که به عنوان یک واحد واحد برای تغییرات داده ها در نظر گرفته می شوند. هر انبوهی دارای ریشه جمع است که تنها نقطه ورودی است که از طریق آن اشیاء خارجی می توانند با مجموعه تعامل داشته باشند. ریشه تضمین می کند که همه قوانین (قوانین) ثابت تجاری اجرا می شوند.
به عنوان مثال، در نمودار بالا:
- در سرویس سفارش،
Orderریشه جمع است. این شاملOrderItem(Entity) وShippingAddress(Value Object) است. سرویس های خارجی نمی توانندOrderItemرا مستقیماً تغییر دهند. آنها باید روشی را در ریشهOrderفراخوانی کنند (به عنوان مثال،order.AddItem())، که تأیید می کند که سفارش قبلاً ارسال نشده است.
ج. رویدادهای دامنه
رویداد دامنه چیزی است که در حوزه ای اتفاق افتاده است که کارشناسان کسب و کار به آن اهمیت می دهند. برای برقراری ارتباط ناهمزمان با تغییرات در میان زمینه های محدود استفاده می شود.
هنگامی که یک Aggregate یک فرمان را اجرا می کند، یک رویداد دامنه (به عنوان مثال، OrderCreated) را منتشر می کند. سایر سرویسها به این رویداد گوش میدهند و وضعیت خود را بر اساس آن بهروزرسانی میکنند، و از ثبات نهایی بدون وابستگیهای API همزمان اطمینان میدهند.
3. یک مثال مشخص: خدمات سفارش در مقابل خدمات موجودی
اجازه دهید به پیادهسازی سادهشده DDD تاکتیکی در Go برای سرویس Order Service نگاهی بیندازیم، که نشان میدهد چگونه Root Aggregate قوانین تجاری را هماهنگ میکند.
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 استراتژیک شروع کنید
استفاده از طراحی دامنه محور برای میکروسرویس ها روشی قدرتمند برای اطمینان از مطابقت معماری شما با ساختار کسب و کار شما است. با این حال، تیمها اغلب اشتباه میکنند که بیش از حد روی الگوهای تاکتیکی تمرکز میکنند (مانند نوشتن رابطهای مخزن و اشیاء ارزشی) در حالی که طراحی استراتژیک را نادیده میگیرند.
اگر مرزها را درست در نظر نگیرید، هیچ کد تاکتیکی تمیزی سیستم شما را از تبدیل شدن به یکپارچه توزیع شده نجات نخواهد داد. همیشه با نقشه برداری استراتژیک شروع کنید - زبان فراگیر خود را تعریف کنید، منطق را در زمینه های محدود گروه بندی کنید، جریان های ارتباطی را ترسیم کنید، و اجازه دهید مرزهای میکروسرویس های شما به طور طبیعی از این تعاریف بیرون بیایند.
معماری نرم افزار، الگوهای طراحی، و بینش های مهندسی بیشتر را در وبلاگ غزنیکس کاوش کنید →