التصميم القائم على المجال في C#: كيف لا تغرق في التعقيد

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

التصميم القائم على المجال في C#: كيف لا تغرق في التعقيد

\n\n

عندما يتجاوز مشروع C# الخاص بك مرحلة "تطبيق CRUD البسيط"، تأتي لحظة الحقيقة. يبدأ منطق الأعمال في التشابك تحت الأقدام، وتنمو أساليب الخدمات إلى مئات الأسطر، وتسبب كلمة "إعادة الهيكلة" رعشة عصبية. هل هذا مألوف؟ مرحبًا بك في عالم حيث بدون التصميم القائم على المجال (DDD) فإنك تخاطر بتحويل الكود إلى مستنقع.

\n\n

DDD ليس مجرد "إطار عمل آخر". إنها فلسفة تضع مجال الأعمال في مركز الكون. اليوم سنشرح كيفية تطبيق التصميم القائم على المجال في مشاريع C# بحيث يصبح الكود ليس فقط عمليًا، بل مفهومًا ومرنًا. لا ماء - فقط أمثلة حقيقية ونصائح عملية.

\n\n

لماذا ينقذ DDD مشاريع C#؟

\n\n

تخيل أنك تكتب نظامًا لإدارة الطلبات في متجر إلكتروني. إذا وضعت كل المنطق في وحدات التحكم ASP.NET، فبعد ستة أشهر لن تستطيع شرح لماذا تستدعي طريقة CreateOrder() طريقة CalculateShipping()، والتي بدورها تصل إلى قاعدة البيانات لأسعار الصرف. يقدم DDD فصلًا واضحًا: هناك طبقة المجال حيث يعيش منطق الأعمال، وهناك البنية التحتية التي تهتم بالتفاصيل التقنية.

\n\n

الفكرة الأساسية لـ DDD هي اللغة الشاملة (Ubiquitous Language). أنت وخبراء الأعمال تتحدثون بلغة واحدة. في الكود، يتم التعبير عن ذلك بمصطلحات مثل: "Order"، "LineItem"، "DiscountPolicy"، وليس "OrderDto"، "OrderService" و "Helper". كلما كان الكود أقرب إلى لغة الأعمال، كان من الأسهل صيانته.

\n\n

التصميم الاستراتيجي والتكتيكي

\n\n

ينقسم DDD إلى جزئين: استراتيجي (كيفية تقسيم النظام إلى وحدات) وتكتيكي (كيفية كتابة الكود داخل الوحدة). في C#، يمنحنا التصميم التكتيكي أربع لبنات بناء قوية:

\n\n
    \n
  • Entity — كائن بمعرف فريد (مثل OrderId).
  • \n
  • Value Object — كائن يُعرف بخصائصه (مثل Money أو Address).
  • \n
  • Aggregate Root — الكيان الجذري الذي يدير مجموعة من الكائنات.
  • \n
  • Repository — تجريد للوصول إلى البيانات.
  • \n
\n\n

دعنا نرى كيف يبدو هذا في الكود.

\n\n

مثال: تصميم طلب (Order)

\n\n

لنبدأ بسيطًا: لدينا طلب يتكون من بنود. يمكن أن يكون الطلب في حالة "جديد"، "مدفوع" أو "ملغي".

\n\n

1. Value Object: Money

\n\n

المال هو Value Object كلاسيكي. كائنان Money متساويان إذا كان لهما نفس المبلغ والعملة. هما غير قابلين للتغيير (immutable).

\n\n
public sealed record Money(decimal Amount, string Currency)\n{\n    public static Money operator +(Money left, Money right)\n    {\n        if (left.Currency != right.Currency)\n            throw new InvalidOperationException("Currency mismatch");\n        return new Money(left.Amount + right.Amount, left.Currency);\n    }\n}\n
\n\n

لاحظ استخدام record — في C# 9+ هذه هي الطريقة المثالية لإنشاء Value Object. فهو يوفر تلقائيًا Equals و GetHashCode والثبات.

\n\n

2. Entity: Order و LineItem

\n\n

للكيانات معرف. لننشئ فئة أساسية لـ Entity:

\n\n
public abstract class Entity<TId>\n{\n    public TId Id { get; protected set; }\n\n    protected Entity(TId id) => Id = id;\n\n    public override bool Equals(object? obj) =>\n        obj is Entity<TId> other && Id!.Equals(other.Id);\n\n    public override int GetHashCode() => Id!.GetHashCode();\n}\n
\n\n

الآن Order و LineItem:

\n\n
public class Order : Entity<Guid>\n{\n    private readonly List<LineItem> _items = new();\n    public IReadOnlyList<LineItem> Items => _items.AsReadOnly();\n    public OrderStatus Status { get; private set; }\n    public Money Total { get; privat                

المدونات

توصيات الكتب