لماذا يتوقف CRUD العادي عن العمل عندما ينمو المشروع
\n\nتخيل: أنت تكتب متجرًا إلكترونيًا باستخدام PHP. في البداية كل شيء بسيط — أضفت منتجًا، غيرت السعر، حذفت. ولكن بعد ستة أشهر، تظهر متطلبات جديدة: تدقيق جميع التغييرات، استعادة البيانات لتاريخ معين، منطق خصم معقد. يبدأ CRUD العادي (إنشاء، قراءة، تحديث، حذف) في الانهيار. تتحول قاعدة البيانات إلى فوضى، وكل طلب عميل جديد يتطلب إعادة كتابة نصف النظام.
\n\nهل هذا مألوف؟ إذن ستفيدك بالتأكيد CQRS (فصل مسؤولية الأوامر والاستعلامات) و Event Sourcing — نمطان قويان يغيران نهج التطوير. في هذه المقالة، سنشرحهما بأمثلة PHP حقيقية، لتتمكن من تطبيقهما في مشاريعك.
\n\nما هو CQRS و Event Sourcing بكلمات بسيطة
\n\nCQRS: فصل الأوامر والاستعلامات
\n\nCQRS هو نمط معماري يفصل العمليات إلى نوعين: الأوامر (تغير الحالة) و الاستعلامات (تقرأ الحالة). في MVC التقليدي، يعمل كل من المتحكم والنموذج مع نفس الكيانات. يقترح CQRS فصل هذه المسؤوليات.
\n\nتخيل مكتبة: هناك طاولة منفصلة لتسجيل الكتب (الأوامر) وطاولة منفصلة للقراءة (الاستعلامات). لا أحد يزعج الآخر، وكل منهما محسّن لمهمته.
\n\nEvent Sourcing: تاريخ التغييرات بدلاً من الحالة الحالية
\n\nEvent Sourcing هو طريقة لتخزين البيانات، حيث لا تحفظ الحالة الحالية للكائن، بل تسلسل جميع الأحداث التي أدت إليها. بدلاً من سطر واحد "الطلب رقم 123: الحالة = تم التوصيل"، تحفظ: "تم إنشاء الطلب ← تم الدفع ← تم الشحن ← تم التوصيل".
\n\nهذا مثل تسجيل فيديو بدلاً من صورة: يمكنك دائمًا الترجيع للخلف ورؤية ما حدث في أي لحظة.
\n\nمتى يجب استخدام CQRS و Event Sourcing في PHP
\n\nهذه الأنماط ليست رصاصة فضية. بالنسبة لمدونة بسيطة، فهي مبالغ فيها. لكن إليك السيناريوهات التي لا غنى عنها فيها:
\n\n- \n
- منطق مجال معقد (المالية، الحجوزات، الخدمات اللوجستية) \n
- متطلبات التدقيق — تحتاج إلى معرفة من ومتى غيّر البيانات \n
- استعادة الحالة في أي تاريخ \n
- بنية الخدمات المصغرة — يمكن نقل الأحداث بسهولة بين الخدمات \n
- حمولة قراءة عالية — يمكن إنشاء نموذج قراءة منفصل محسّن للاستعلامات \n
إذا كان مشروعك متجرًا إلكترونيًا بآلاف الطلبات، أو نظام CRM، أو تطبيقًا مصرفيًا — فإن CQRS و Event Sourcing سيفيدانك بالتأكيد.
\n\nالتنفيذ العملي لـ CQRS في PHP
\n\nدعنا ننتقل إلى الكود. سننفذ نظامًا بسيطًا لإدارة الطلبات. نبدأ بـ CQRS.
\n\nالخطوة 1: تحديد الأوامر والاستعلامات
\n\nالأمر هو DTO (كائن نقل البيانات) يحتوي على جميع البيانات اللازمة لتنفيذ العملية. الأمر لا يعيد البيانات أبدًا — فقط يغير الحالة.
\n\n<?php\n// src/Application/Command/CreateOrderCommand.php\nnamespace App\\Application\\Command;\n\nclass CreateOrderCommand\n{\n public function __construct(\n public readonly string $orderId,\n public readonly string $customerId,\n public readonly array $items, // [['productId' => '...', 'quantity' => 2], ...]\n public readonly \\DateTimeImmutable $createdAt\n ) {}\n}\n\n\nالاستعلام هو أيضًا DTO، لكن للقراءة. يمكن أن يحتوي على عوامل تصفية ويعيد البيانات.
\n\n<?php\n// src/Application/Query/GetOrderQuery.php\nnamespace App\\Application\\Query;\n\nclass GetOrderQuery\n{\n public function __construct(\n public readonly string $orderId\n ) {}\n}\n\n\nالخطوة 2: إنشاء معالجات الأوامر
\n\nمعالج الأمر هو خدمة تنفذ منطق الأعمال. يستقبل الأمر ويغير الحالة