التصميم القائم على المجال في C++: بنية للمشاريع المعقدة

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

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

ابدأ الدورة

لماذا DDD ليس عن الرسوم البيانية المملة، بل عن بنية حية

\n

عندما يتجاوز مشروع C++ حجم تطبيق "لعبة"، غالبًا ما يواجه المطورون الفوضى: منطق الأعمال منتشر عبر المتحكمات (controllers)، قاعدة البيانات تفرض هيكل الفئات، والمبتدئون يخشون لمس الكود القديم (legacy). التصميم القائم على المجال (DDD) ليس مجموعة من القواعد المجردة، بل أداة عملية تساعد في السيطرة على التعقيد. في هذه المقالة، سنشرح كيفية تطبيق DDD في C++ باستخدام أمثلة كود محددة، ولماذا سينقذ مشروعك من "كتلة الطين الكبيرة".

\n\n

ما هو DDD ولماذا يحتاجه مطور C++؟

\n

التصميم القائم على المجال (DDD) هو نهج يركز على نموذج المجال (domain model) بدلاً من التفاصيل التقنية. في C++، هذا مهم بشكل خاص: اللغة توفر أدوات قوية (القوالب (templates)، RAII، المؤشرات الذكية)، ولكن بدون بنية صحيحة، يتحول المشروع إلى كتلة واحدة (monolith) حيث يؤدي تغيير ميزة واحدة إلى كسر كل شيء.

\n

يقدم DDD مستويين:

\n
    \n
  • النمذجة الاستراتيجية: تقسيم النظام إلى سياقات (Bounded Contexts) وضبط تفاعلها.
  • \n
  • النمذجة التكتيكية: اللبنات الأساسية — Entity، Value Object، Aggregate، Repository، Domain Service.
  • \n
\n

سنركز على النمذجة التكتيكية، لأنها توفر أنماطًا محددة للكود في C++.

\n\n

البنية الطبقية: أساس DDD في C++

\n

يفترض DDD تقسيمًا واضحًا إلى طبقات. الهيكل النموذجي لمشروع C++ يبدو كالتالي:

\n
    \n
  • طبقة المجال (Domain Layer - النواة): الكيانات (entities)، كائنات القيمة (value objects)، المجاميع (aggregates)، خدمات المجال (domain services). لا توجد تبعيات لأطر العمل (frameworks) أو المكتبات الخارجية.
  • \n
  • طبقة التطبيق (Application Layer): حالات الاستخدام (use cases)، التنسيق. تعتمد فقط على طبقة المجال.
  • \n
  • طبقة البنية التحتية (Infrastructure Layer): تنفيذ المستودعات (repositories) (SQL، Redis، ملفات)، العمل مع واجهات برمجة التطبيقات الخارجية (APIs). تنفذ الواجهات من طبقة المجال.
  • \n
  • طبقة العرض (Presentation Layer): واجهة المستخدم (وحدة التحكم، REST API، واجهة رسومية).
  • \n
\n

هذه ليست قاعدة صارمة، بل توصية. الشيء الرئيسي هو أن التبعيات تتجه إلى الداخل: يمكن للطبقات الخارجية أن تعتمد على الداخلية، ولكن ليس العكس.

\n\n

مثال: كيف لا يجب أن تفعل

\n
// ❌ سيء: منطق الأعمال مختلط مع SQL\nclass UserController {\n    void createUser(const std::string& name) {\n        // استعلام SQL هنا مباشرة\n        db.execute("INSERT INTO users (name) VALUES (?)", name);\n        // منطق التحقق هنا أيضًا\n        if (name.empty()) throw std::runtime_error("Name required");\n    }\n};\n
\n\n

مثال: أسلوب DDD

\n
// ✅ جيد: فصل واضح\nclass User : public Entity<User, int> {\n    std::string name_;\npublic:\n    explicit User(int id, std::string name) : Entity(id), name_(std::move(name)) {\n        if (name_.empty()) throw std::invalid_argument("User name cannot be empty");\n    }\n    const std::string& name() const { return name_; }\n};\n\n// واجهة المستودع (طبقة المجال)\nclass IUserRepository {\npublic:\n    virtual ~IUserRepository() = default;\n    virtual void save(const User& user) = 0;\n};\n\n// التنفيذ (طبقة البنية التحتية)\nclass SqlUserRepository : public IUserRepository {\n    void save(const User& user) override {\n        // فقط SQL، بدون منطق أعمال\n    }\n};\n
\n\n

اللبنات الأساسية لـ DDD في C++

\n\n

Entity (الكيان)

\n

هذا كائن له معرّف فريد يستمر عبر الزمن. في C++، من المناسب إنشاء فئة أساسية قالبية:

\n
template<typename T, typename IdType>\nclass Entity {\n    IdType id_;\npublic:\n    explicit Entity(IdType id) : id_(std::move(id)) {}\n    IdType id() const { return id_; }\n    bool operator==(const Entity& other) const { return id_ == other.id_; }\n                

المدونات

توصيات الكتب