يا جماعة الخير، السلام عليكم ورحمة الله وبركاته.
اسمحوا لي اليوم أحكي لكم قصة صارت معي قبل كم سنة، قصة بتلخص حال كثير من المشاريع البرمجية اللي بتبدأ بحماس وبتنتهي بكابوس. كنا وقتها في شركة ناشئة، والحمد لله، منتجنا كان ماشي زي النار في الهشيم. تطبيقنا كان عبارة عن منصة تجارة إلكترونية، وبدأنا بشكل بسيط جدًا: منتجات، سلة شراء، دفع. الأمور كانت تمام التمام.
مع النجاح، كبرت الأحلام وبدأت الطلبات “البسيطة” تنهال علينا من قسم الأعمال: “أبو عمر، بدنا نضيف كوبونات خصم”، “يا ريت لو نعمل نظام نقاط ولاء للعملاء”، “شو رأيك نضيف خيار الشحن السريع؟”، “لازم يكون في تقييمات للمنتجات”. وكل طلب من هدول، كنا نقول “بسيطة” و”تكرم”.
المشكلة إنه كل “بسيطة” كانت عبارة عن إضافة طبقة جديدة من الطين على “كرة” الكود تبعنا. بعد سنة، صار الوضع لا يُطاق. الكود صار عبارة عن “كرة طين كبيرة” (Big Ball of Mud) زي ما بحكوا الأجانب. تغيير بسيط في نظام الخصومات كان يضرب نظام الشحن. تعديل في صفحة المنتج كان يسبب مشاكل في تقارير المبيعات. صار الفريق يخاف يلمس الكود. كل مهمة جديدة كانت تحتاج اجتماعات طويلة بس عشان نفهم وين ممكن نكتب الكود الجديد بدون ما نكسر الدنيا.
في اجتماع من الاجتماعات، وأنا شايف الإحباط على وجوه الفريق، واحد من المبرمجين الجداد حكى جملة بعدها بترن في بالي: “يا جماعة، إحنا مش فاهمين البيزنس تبعنا… كودنا ما بحكي نفس لغة قسم المبيعات”. والله يا جماعة كانت زي الصفعة اللي صحتني. المشكلة ما كانت في الكود نفسه، المشكلة كانت في الطريقة اللي بنفكر فيها وبننظم فيها الكود. هنا كانت بداية رحلتنا مع عالم الـ Domain-Driven Design (DDD).
ما هو التصميم الموجه بالمجال (DDD)؟ وليش هو طوق النجاة؟
بكل بساطة، التصميم الموجه بالمجال هو مش مجرد مجموعة أدوات أو مكتبات برمجية، هو فلسفة ومنهجية في بناء البرمجيات. جوهر الفكرة هو: خلي تركيزك الأساسي على “المجال” أو “البيزنس” نفسه، مش على التكنولوجيا.
الهدف هو إنشاء نموذج برمجي (Software Model) يكون انعكاسًا دقيقًا لنموذج العمل (Business Domain)، واستخدام لغة مشتركة وواضحة بين المبرمجين وخبراء المجال (Business Experts).
يعني بدل ما يكون المبرمج في واد، ورجال الأعمال والتسويق في واد ثاني، الـ DDD بجبرنا كلنا نحكي نفس اللغة. هذه اللغة بنسميها “اللغة المنتشرة في كل مكان” أو Ubiquitous Language. لما قسم المبيعات يحكي “عميل مميز”، لازم يكون عنا في الكود كلاس أو مفهوم اسمه “عميل مميز” (PremiumCustomer) بنفس الخصائص والقواعد اللي همه بقصدوها.
الأدوات الاستراتيجية للـ DDD: تفكيك “كرة الطين”
أول خطوة في رحلتنا كانت استخدام الأدوات الاستراتيجية للـ DDD عشان نفهم الصورة الكبيرة ونقسم “كرة الطين” لقطع أصغر وأسهل في التعامل. هاي الأدوات بتساعدنا نرسم خريطة للمشروع.
السياق المحدود (Bounded Context)
هذا هو المفهوم الأهم على الإطلاق في DDD. السياق المحدود هو عبارة عن رسم حدود واضحة حول جزء معين من النظام. داخل كل سياق، بيكون في نموذج واحد ولغة واحدة واضحة. خلينا نرجع لمثال منصة التجارة الإلكترونية تبعتنا:
- سياق المبيعات (Sales Context): هنا، “المنتج” (Product) هو شيء له سعر ووصف وصورة. “العميل” (Customer) هو شخص له سلة مشتريات وتاريخ طلبات. اللغة هنا كلها بتدور حول البيع والشراء.
- سياق المخزون (Inventory Context): هنا، “المنتج” (Product) هو عبارة عن SKU (رمز تعريف) وكمية متوفرة في المستودع وموقعه. “العميل” مش موجود أصلًا في هذا السياق.
- سياق الدعم الفني (Support Context): هنا، “العميل” (Customer) هو شخص عنده “تذاكر دعم” (Support Tickets)، و”المنتج” (Product) هو شيء ممكن يكون سبب في شكوى أو استفسار.
شايفين كيف كلمة “منتج” إلها معاني مختلفة تمامًا في كل سياق؟ لما فصلنا هاي السياقات، صار كل فريق مسؤول عن سياقه الخاص، والكود صار أبسط وأوضح ألف مرة. هذا الفصل هو أول خطوة عملية نحو معمارية الخدمات المصغرة (Microservices).
خريطة السياقات (Context Map)
بعد ما حددنا السياقات، لازم نرسم خريطة توضح كيف هاي السياقات بتتكلم مع بعض. هل سياق المبيعات بيعتمد على سياق المخزون عشان يعرف الكمية المتوفرة؟ أكيد. هل سياق الدعم الفني بيحتاج معلومات من سياق المبيعات عن طلبات العميل؟ نعم. هاي الخريطة بتوضح العلاقات بين الفرق المختلفة وبتمنع الفوضى.
الأدوات التكتيكية للـ DDD: بناء الكود من الداخل
بعد ما رسمنا الصورة الكبيرة، بيجي دور كتابة الكود الفعلي داخل كل سياق محدود. هنا الـ DDD بيعطينا مجموعة من الأنماط (Patterns) اللي بتساعدنا نكتب كود نظيف، منظم، ويعكس منطق العمل (Business Logic) بشكل صحيح.
الكيانات (Entities)
الكيان هو أي كائن في النظام إله هوية فريدة ومستمرة عبر الزمن، حتى لو تغيرت خصائصه. أهم شيء فيه هو “الهوية” (Identity).
مثال: العميل (Customer). هو نفس العميل حتى لو غير عنوانه أو رقم تلفونه. بنعرفه من خلال رقم العميل (CustomerID).
// مثال بسيط لكيان العميل بلغة تشبه C#
public class Customer : Entity
{
public Guid Id { get; private set; } // الهوية الفريدة
public string Name { get; private set; }
public Address ShippingAddress { get; private set; }
public Customer(Guid id, string name)
{
Id = id;
Name = name;
}
public void ChangeAddress(Address newAddress)
{
// هنا ممكن نضيف أي قواعد عمل (Business Rules)
// مثلاً: لا يمكن تغيير العنوان إذا كان هناك شحنة قيد التوصيل
this.ShippingAddress = newAddress;
}
}
كائنات القيمة (Value Objects)
على عكس الكيانات، كائنات القيمة هي كائنات ما إلها هوية، وتُعرَّف بخصائصها فقط. هي كائنات غير قابلة للتغيير (Immutable).
مثال: العنوان (Address). عنوانين متطابقين (نفس الشارع، المدينة، الدولة) هما نفس الشيء. لو بدك تغير العنوان، أنت بتستبدل الكائن القديم بكائن جديد تمامًا.
// مثال لكائن القيمة "العنوان"
public class Address : ValueObject
{
public string Street { get; }
public string City { get; }
public string Country { get; }
public Address(string street, string city, string country)
{
// يمكن إضافة تحقق هنا، مثلاً لا يمكن أن يكون الشارع فارغاً
Street = street;
City = city;
Country = country;
}
// يجب تعريف طرق للمساواة بناءً على القيم
}
التجميعات (Aggregates)
وهنا سر السيطرة على التعقيد. التجميعة هي مجموعة من الكيانات وكائنات القيمة اللي بنعاملها كوحدة واحدة متكاملة. كل تجميعة إلها “جذر” (Aggregate Root)، وهو الكيان الوحيد اللي مسموح للكود الخارجي يتعامل معه.
أهم قاعدة: أي تعديل على أي كائن داخل التجميعة لازم يتم من خلال جذر التجميعة. هذا بيضمن تطبيق كل قواعد العمل (Business Rules) بشكل صحيح.
مثال: الطلب (Order) هو جذر تجميعة. التجميعة ممكن تحتوي على قائمة من “بنود الطلب” (OrderLine). ما بنفع نضيف بند طلب مباشرة، لازم نطلب من كائن “الطلب” إنه يضيفه.
public class Order : AggregateRoot
{
public Guid Id { get; private set; }
private readonly List _orderLines = new List();
public decimal TotalPrice { get; private set; }
public OrderStatus Status { get; private set; }
// ... constructor ...
public void AddProduct(Product product, int quantity)
{
// قاعدة عمل 1: لا يمكن إضافة منتج لطلب تم شحنه
if (this.Status == OrderStatus.Shipped)
{
throw new Exception("لا يمكن تعديل طلب تم شحنه.");
}
// قاعدة عمل 2: حساب السعر الإجمالي
var line = new OrderLine(product.Id, quantity, product.Price);
_orderLines.Add(line);
RecalculateTotalPrice();
}
public void Ship()
{
// ... منطق الشحن ...
this.Status = OrderStatus.Shipped;
}
private void RecalculateTotalPrice()
{
// ... حساب السعر ...
}
}
بهذه الطريقة، جذر التجميعة (Order) بيحمي “الثوابت” (Invariants) الخاصة فيه. مستحيل يكون عندك طلب بحالة “تم الشحن” وبنفس الوقت تقدر تضيف عليه منتجات جديدة. الكود نفسه صار يفرض قواعد العمل.
نصائح عملية من خبرة أبو عمر
بعد ما خضنا هاي التجربة، تعلمت كم شغلة مهمة بحب أشارككم إياها:
- الـ DDD رحلة، مش وجهة: ما تتوقع تطبق كل هاي المفاهيم بيوم وليلة. ابدأ بسياق واحد (Bounded Context) يكون مهم ومعقد. لما تنجح فيه، انتقل للي بعده.
- احكي مع أهل الخبرة: أهم جزء في الـ DDD هو بناء “اللغة المشتركة”. اقعد مع قسم المبيعات، المحاسبة، الدعم… اسمع منهم، افهم مصطلحاتهم، وخلي كودك يعكس هذا الفهم. المبرمج الشاطر مش اللي بيكتب كود معقد، بل اللي بيكتب كود مفهوم لأهل البيزنس.
- مش كل المشاريع بتحتاج DDD: إذا بتعمل موقع بسيط أو تطبيق CRUD (إنشاء، قراءة، تحديث، حذف) مباشر، يمكن الـ DDD يكون تعقيد زيادة عن اللزوم. الـ DDD بيلمع نجمه في الأنظمة المعقدة اللي فيها منطق عمل (Business Logic) متشعب.
- النموذج يتطور: النموذج اللي بتبنيه اليوم مش مقدس. مع تغير البيزنس، لازم النموذج يتغير ويتطور. خليك مرن ومستعد لإعادة التفكير في تصميمك (Refactoring).
الخلاصة: من الفوضى إلى النظام 🚀
في النهاية، رحلتنا مع التصميم الموجه بالمجال (DDD) ما كانت سهلة، وبدها تغيير في طريقة التفكير قبل تغيير الكود. لكن النتيجة كانت تستحق كل التعب. انتقلنا من “كرة طين كبيرة” الكل بخاف يقرب عليها، لنظام مقسم ومنظم، كل جزء فيه واضح ومفهوم، والكود نفسه صار وثيقة حية تشرح منطق العمل.
نصيحتي الأخيرة لكل مطور ومطورة: استثمروا وقت في فهم “المجال” اللي بتشتغلوا فيه بنفس قدر الوقت اللي بتستثمروه في تعلم أحدث إطار عمل (Framework). القوة الحقيقية مش في الأدوات، بل في قدرتنا على تحويل منطق العمل المعقد إلى كود بسيط، أنيق، وقابل للصيانة. وهذا هو جوهر الـ DDD.
أتمنى لكم كل التوفيق في مشاريعكم، وربنا يبعد عنكم “كرات الطين”!