تصميم قائم على المجال DDD في PHP: كيف تحول الفوضى إلى نظام
\n\nهل شعرت يومًا أن مشروع PHP الخاص بك يكبر مثل كرة الثلج؟ في البداية كل شيء بسيط: بضع وحدات تحكم، نموذج، وعدد قليل من استعلامات قاعدة البيانات. ولكن بعد ستة أشهر، تجد نفسك غارقًا في كود spaghetti حيث تكون منطق الأعمال موزعًا عبر الخدمات، وتغيير "بسيط" في المتطلبات يستغرق أسبوعًا كاملاً.
\n\nهل يبدو هذا مألوفًا؟ مرحبًا بك في النادي. لكن هناك خبر سار: Domain-Driven Design (DDD) ليس مجرد نظرية مجردة من كتب إريك إيفانز السميكة. إنها أداة عمل حقيقية تساعدك على كتابة كود PHP مفهوم لكل من رجال الأعمال والمطورين.
\n\nفي هذا المقال، سنشرح DDD عمليًا. بدون حشو، مع أمثلة حية بلغة PHP. ستتعلم كيفية تطبيق DDD في مشروعك دون أن تصاب بالصداع أو إعادة كتابة كل شيء من الصفر. هيا بنا!
\n\nما هو Domain-Driven Design ولماذا يحتاجه مطور PHP؟
\n\nتخيل أنك تبني متجرًا إلكترونيًا. لديك خصومات، سلة تسوق، وطلبات. منطق الأعمال: "الخصم لا يتكدس مع كود الخصم"، "لا يمكن إلغاء الطلب بعد الشحن". إذا كنت تخزن هذا المنطق في وحدات التحكم أو توزعه عبر نماذج ORM، فستصاب بالارتباك عاجلاً أم آجلاً.
\n\nDDD هو نهج يضع مجال الأعمال (مجالك الموضوعي) في مركز التطوير. بدلاً من التفكير في الجداول واستعلامات SQL، تفكر في الكيانات والقيم والأحداث.
\n\nبالنسبة لـ PHP، هذا مهم بشكل خاص. اللغة مرنة، ولكن بدون بنية واضحة، تتحول المشاريع بسرعة إلى "كود قديم". يمنحك DDD إطارًا يبقى قابلاً للقراءة بعد عام.
\n\nالتصميم الاستراتيجي مقابل التصميم التكتيكي
\n\nينقسم DDD إلى جزأين رئيسيين:
\n- \n
- التصميم الاستراتيجي — هو خريطة مشروعك. تحدد السياقات المحدودة (Bounded Contexts)، وتبني اللغة الشاملة (Ubiquitous Language). على سبيل المثال، في سياق "الكتالوج"، المنتج هو مجرد مجموعة من الخصائص، بينما في سياق "الطلب"، هو وحدة ذات سعر وكمية. \n
- التصميم التكتيكي — هو اللبنات الأساسية: Entity، Value Object، Aggregate، Repository، Domain Service، Domain Event. سنركز عليها. \n
التصميم التكتيكي في PHP: لبنات DDD
\n\nدعنا نرى كيف تبدو أنماط DDD الرئيسية في PHP. انسَ ActiveRecord من Laravel أو Symfony — هنا نبني نموذجًا مستقلاً عن الإطار.
\n\n1. Entity (الكيان)
\nالكيان هو كائن له معرف فريد ويتغير بمرور الوقت. على سبيل المثال، User أو Order. نقارن الكيانات بالمعرف، وليس بالحقول.
<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Domain\\Entity;\n\nuse App\\Domain\\ValueObject\\UserId;\nuse App\\Domain\\ValueObject\\Email;\n\nclass User\n{\n public function __construct(\n private UserId $id,\n private string $name,\n private Email $email\n ) {}\n\n public function getId(): UserId\n {\n return $this->id;\n }\n\n public function changeEmail(Email $newEmail): void\n {\n // هنا قد يكون منطق الأعمال: التحقق مما إذا كان البريد الإلكتروني مشغولاً\n $this->email = $newEmail;\n }\n\n public function getEmail(): Email\n {\n return $this->email;\n }\n}\n\n\n2. Value Object (كائن القيمة)
\nValue Object هو كائن غير قابل للتغيير يتم تعريفه بخصائصه، وليس بمعرفه. كائنان Value Object متساويان إذا تطابقت جميع حقولهما. أمثلة كلاسيكية: Money، Email، Address.
<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Domain\\ValueObject;\n\nclass Email\n{\n private string $value;\n\n public function __construct(string $value)\n {\n if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {\n throw new \\InvalidArgumentException('عنوان بريد إلكتروني غير صالح');\n }\n $thi