التصميم المبني على المجال (DDD) في الخدمات المصغرة
عندما تنتقل المؤسسات من البنية المتجانسة إلى الخدمات الصغيرة، فإنها تواجه سؤالًا بالغ الأهمية وشديد المخاطر: كيف نرسم حدود خدماتنا؟
من الناحية النظرية، يجب أن تكون الخدمات الصغيرة وحدات فضفاضة ومنفصلة يمكن تطويرها ونشرها وتوسيع نطاقها بشكل مستقل. ومع ذلك، من الناحية العملية، ينتهي الأمر بالعديد من الفرق إلى إنشاء وحدة متراصة موزعة — وهو نظام تكون فيه الخدمات مقترنة بإحكام بحيث يتطلب تغيير عمل واحد تعديل ونشر خدمات متعددة في وقت واحد، مما يؤدي إلى تفاقم زمن استجابة الشبكة واختناقات النشر.
لتجنب هذا المأزق، يلجأ مهندسو البرمجيات إلى التصميم المعتمد على المجال (DDD). تم تقديم DDD لأول مرة بواسطة Eric Evans في عام 2003، وهي عبارة عن منهجية لتطوير البرمجيات تعمل على مواءمة هياكل التعليمات البرمجية مع مجالات الأعمال المعقدة التي تمثلها.
في هذه المقالة، سنستكشف كيف يوفر DDD المخططات الإستراتيجية والتكتيكية لتصميم خدمات صغيرة نظيفة ومنفصلة وقابلة للصيانة بدرجة كبيرة.
1. المخطط الاستراتيجي: رسم حدود الخدمة
ينقسم DDD إلى مرحلتين أساسيتين: التصميم الاستراتيجي والتصميم التكتيكي. يدور التصميم الاستراتيجي حول نمذجة سياقات العمل وتحديد حدود الخدمة. فهو يوفر عرض “ماكرو” للهندسة المعمارية.
أ. اللغة المنتشرة في كل مكان
داخل الشركة، تستخدم الأقسام المختلفة نفس الكلمات لتعني أشياء مختلفة تمامًا. على سبيل المثال:
- بالنسبة إلى فريق المبيعات، فإن “المستخدم” هو عميل محتمل أو عميل محتمل.
- بالنسبة إلى فريق الأمان، “المستخدم” هو مجموعة من بيانات اعتماد تسجيل الدخول.
- بالنسبة إلى فريق الشحن، “المستخدم” هو عنوان المستلم الفعلي.
إن محاولة إنشاء نموذج قاعدة بيانات واحد وموحد لـ “المستخدم” الذي يرضي الجميع يؤدي إلى قاعدة بيانات ضخمة ومتشابكة. يحل DDD هذه المشكلة من خلال إنشاء لغة في كل مكان — وهي مفردات مشتركة يتم تعريفها واستخدامها من قبل كل من خبراء مجال الأعمال والمطورين ضمن حدود معينة.
ب. السياقات المحدودة
السياق المحدود هو الحد الصريح الذي ينطبق ضمنه نموذج المجال. داخل الحدود، جميع المصطلحات في اللغة المنتشرة لها معنى واحد لا لبس فيه.
بدلاً من نموذج “مستخدم” واحد، نقوم بتعريف نماذج منفصلة ضمن السياقات المحددة الخاصة بها:
- في سياق الطلب، لدينا نموذج
Orderيحتوي على معلومات الاتصال بالعميل. - في سياق الهوية، لدينا نموذج
Credentials. - في سياق الشحن، لدينا نموذج
DeliveryAddress.
القاعدة الذهبية للخدمات الصغيرة: يرتبط السياق المحدود مباشرة بخدمة صغيرة واحدة.
من خلال تقسيم النظام إلى سياقات محددة، فإننا نضمن أن الخدمات الصغيرة مقترنة بشكل غير محكم وتمثل قدرات أعمال متميزة.
ج. رسم خرائط السياق: كيفية تواصل الخدمات
السياقات المقيدة لا توجد بمعزل عن غيرها؛ يجب أن يتفاعلوا. تحدد خريطة السياق العلاقات وآليات الترجمة بين السياقات. تشمل الأنماط الرئيسية ما يلي:
- النواة المشتركة: يتشارك سياقان في مجموعة فرعية صغيرة من نموذج المجال وقاعدة البيانات (عادةً ما لا يُنصح باستخدام الخدمات الصغيرة بسبب الاقتران).
- العميل والمورد: يجب أن يقدم سياق واحد (المورد) البيانات إلى آخر (العميل). يجب على المورد تنسيق جداول الإصدار مع العميل.
- طبقة مكافحة الفساد (ACL): طبقة ترجمة تترجم البيانات الواردة من نظام خارجي إلى نموذج المجال الداخلي للعميل، مما يمنع النماذج الخارجية من تلويث البنية الداخلية.
2. المخطط التكتيكي: هيكلة قاعدة بيانات الخدمات الصغيرة
بمجرد رسم حدود الخدمة باستخدام التصميم الاستراتيجي، يوفر التصميم التكتيكي مجموعة من أنماط التصميم لهيكلة التعليمات البرمجية داخل خدمة صغيرة واحدة.
أ. الكيانات وأشياء القيمة
داخل الخدمة المصغرة، يتم تقسيم النماذج إلى فئتين:
- الكيانات: الكائنات التي لها هوية فريدة تستمر بمرور الوقت، حتى لو تغيرت سماتها. تتضمن الأمثلة
OrderأوProduct. - كائنات القيمة: الكائنات التي ليس لها هوية فريدة ويتم تعريفها بالكامل من خلال سماتها. إنهم غير قابلين للتغيير. تتضمن الأمثلة
ShippingAddressأوProductDimensions. إذا كان لكائنين قيمة نفس السمات، فإنهما يعتبران متساويين.
ب. الركام والجذور الركامية
التجميع عبارة عن مجموعة من الكيانات المرتبطة وكائنات القيمة التي يتم التعامل معها كوحدة واحدة لتغييرات البيانات. يحتوي كل تجميع على جذر إجمالي، وهو نقطة الدخول الوحيدة التي يمكن من خلالها للكائنات الخارجية التفاعل مع التجميع. يضمن الجذر تطبيق جميع ثوابت الأعمال (القواعد).
على سبيل المثال، في الرسم البياني أعلاه:
- في خدمة الطلب،
Orderهو الجذر التجميعي. يحتوي علىOrderItem(الكيان) وShippingAddress(كائن القيمة). لا يمكن للخدمات الخارجية تعديلOrderItemمباشرةً؛ يجب عليهم استدعاء طريقة على الجذرOrder(على سبيل المثال،order.AddItem())، والتي تتحقق من أن الطلب لم يتم شحنه بالفعل.
ج. أحداث المجال
حدث المجال هو شيء حدث في المجال الذي يهتم به خبراء الأعمال. يتم استخدامه لتوصيل التغييرات عبر السياقات المحدودة بشكل غير متزامن.
عندما ينفذ التجميع أمرًا، فإنه ينشر حدث المجال (على سبيل المثال، OrderCreated). تستمع الخدمات الأخرى إلى هذا الحدث وتقوم بتحديث حالتها وفقًا لذلك، مما يضمن الاتساق النهائي دون تبعيات واجهة برمجة التطبيقات المتزامنة.
3. مثال ملموس: خدمة الطلب مقابل خدمة المخزون
دعونا نلقي نظرة على التنفيذ المبسط لـ DDD التكتيكي في Go لـ Order Service، والذي يوضح كيفية تنسيق Aggregate Root لقواعد العمل.
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 }
]
}
}
تستمع Inventory Service إلى هذا الحدث، وتقوم بتحديث كيان StockLevel المحلي الخاص بها، وتكمل التدفق بشكل غير متزامن.
4. DDD الاستراتيجي مقابل التكتيكي في الخدمات المصغرة
| المرحلة / الجانب | استراتيجية DDD | DDD التكتيكي |
|---|---|---|
| النطاق | بنية النظام العالمي (ماكرو) | داخل قاعدة بيانات خدمة واحدة (مايكرو) |
| الهدف الأساسي | رسم حدود خدمة نظيفة ومنفصلة | نموذج منطق الأعمال الغني وفرض الثوابت |
| المفاهيم الأساسية | السياقات المحدودة، اللغة المنتشرة، خريطة السياق | الكيانات، كائنات القيمة، المجاميع، أحداث المجال |
| الجمهور | المهندسين المعماريين ومديري المنتجات والمطورين | مطورو البرمجيات، مراجعو الأكواد |
| التأثير على الخدمات الصغيرة | يحدد عدد ونطاق الخدمات | يحدد معاملات قاعدة البيانات وبنية الدليل |
الخلاصة: ابدأ بـ DDD الاستراتيجي أولاً
يعد تطبيق التصميم المستند إلى المجال على الخدمات الصغيرة طريقة فعالة لضمان تطابق البنية الخاصة بك مع بنية عملك. ومع ذلك، غالبًا ما ترتكب الفرق خطأ التركيز كثيرًا على الأنماط التكتيكية (مثل كتابة واجهات المستودعات وأشياء القيمة) مع تجاهل التصميم الاستراتيجي.
إذا لم تضع الحدود بشكل صحيح، فلن يتمكن أي قدر من التعليمات البرمجية التكتيكية النظيفة من إنقاذ نظامك من التحول إلى وحدة متراصة موزعة. ابدأ دائمًا بالتخطيط الاستراتيجي — حدد لغتك الشاملة، وقم بتجميع المنطق في سياقات محددة، ورسم خريطة لتدفقات الاتصال، ودع حدود الخدمات الصغيرة الخاصة بك تظهر بشكل طبيعي من تلك التعريفات.
استكشف المزيد من بنية البرامج وأنماط التصميم والرؤى الهندسية على مدونة غازنيكس →