CQRS و Event Sourcing في Rust: بنية للأداء الذي لا يقبل المساومة

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

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

ابدأ الدورة

لماذا CQRS و Event Sourcing ليسا "إفراطًا في الهندسة" بل إنقاذًا للأنظمة المعقدة

\n\n

هل سبق لك أن واجهت موقفًا حيث يتعامل نظامك المكتوب بلغة Rust بشكل مثالي مع طلبات القراءة، ولكنه "ينهار على ركبتيه" عند الكتابة؟ أو العكس - تضحي بأداء القراءة من أجل القدرة على حفظ السجل الكامل للتغييرات؟ هل يبدو ذلك مألوفًا؟ إذن مرحبًا بك في عالم CQRS و Event Sourcing.

\n\n

يعتبر العديد من المطورين هذه الأنماط "مدفعية ثقيلة"، قابلة للتطبيق فقط في مشاريع المؤسسات بلغة C# أو Java. ولكن مع تزايد شعبية Rust في الأنظمة عالية التحميل (من خوادم الألعاب إلى المنصات المالية)، أصبحت CQRS و Event Sourcing ليست مفيدة فحسب - بل أصبحت ضرورية. Rust مع تكلفتها الصفرية للتجريد والكتابة الصارمة هي اللغة المثالية لتنفيذ هذه الأنماط.

\n\n

في هذه المقالة، لن نتحدث عن النظرية فقط. سنكتب كودًا عمليًا بلغة Rust يمكنك تكييفه مع مشاريعك. ستتعلم كيفية فصل المسؤولية بين الأوامر والاستعلامات، وكيفية تخزين الأحداث بدلاً من الحالة الحالية، وكيف يسرع كل ذلك نظامك أضعافًا مضاعفة.

\n\n

ما هما CQRS و Event Sourcing؟ بلغة بسيطة لمطور Rust

\n\n

تخيل أنك تدير حسابًا بنكيًا. في نموذج CRUD التقليدي، لديك جدول واحد "حسابات" حيث يتم تخزين الرصيد الحالي. إذا كنت بحاجة إلى عرض السجل، فستقوم بإنشاء جدول آخر "المعاملات". ولكن ماذا لو كنت لا تحتاج فقط إلى معرفة الرصيد، بل إلى فهم لماذا تغير؟ أو استعادة الحالة في أي تاريخ؟

\n\n

CQRS: فرق تسد

\n\n

CQRS (Command Query Responsibility Segregation) هو مبدأ يفصل العمليات إلى مجموعتين:

\n
    \n
  • الأوامر (Commands) - تغير الحالة. لا تعيد بيانات، فقط تأكيدًا أو خطأ.
  • \n
  • الاستعلامات (Queries) - تقرأ البيانات فقط. لا تغير الحالة.
  • \n
\n\n

في Rust، يبدو هذا الفصل طبيعيًا: أنواع الأوامر والاستعلامات هي هياكل مختلفة، ومعالجاتها هي دوال أو وحدات مختلفة. هذه ليست مجرد "ممارسة جيدة"، بل هي طريقة لتجنب التغييرات العشوائية أثناء القراءة.

\n\n
// الأمر - يغير الحالة\npub struct CreateOrder {\n    pub user_id: Uuid,\n    pub items: Vec<OrderItem>،\n}\n\n// الاستعلام - يقرأ فقط\npub struct GetOrderHistory {\n    pub user_id: Uuid,\n    pub limit: usize،\n}
\n\n

Event Sourcing: احفظ السجل، وليس لقطة لحظية

\n\n

Event Sourcing هو نهج حيث لا نحفظ الحالة الحالية للكائن، بل تسلسل الأحداث التي أدت إليها. هل تريد معرفة رصيد الحساب في 1 يناير 2023؟ فقط "شغّل" جميع الأحداث من البداية حتى ذلك التاريخ.

\n\n

المزايا واضحة:

\n
    \n
  • التدقيق - كل تغيير مسجل.
  • \n
  • اللقطات الزمنية - يمكن استعادة أي حالة.
  • \n
  • مقاومة الأخطاء - إذا كان هناك خطأ في الكود، لم تفقد البيانات، بل قمت فقط "بتشغيلها" بشكل غير صحيح.
  • \n
\n\n

مثال عملي: نظام إدارة الطلبات بلغة Rust

\n\n

دعونا نبني نظامًا مصغرًا لمتجر إلكتروني. سيعالج الأوامر (إنشاء طلب، إضافة منتج) والاستعلامات (الحصول على سلة التسوق، سجل الطلبات). كل المنطق سيكون مبنيًا على الأحداث.

\n\n

الخطوة 1: تحديد الأحداث والأوامر

\n\n

أول ما يجب فعله هو تصميم المجال. في Rust، نستخدم التعدادات (enum) للأحداث والهياكل الصارمة للأوامر.

\n\n
use serde::{Deserialize, Serialize};\nuse uuid::Uuid;\n\n// الأحداث - حقائق غير قابلة للتغيير\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum OrderEvent {\n    Created { order_id: Uuid, user_id: Uuid, timestamp: i64 },\n    ItemAdded { order_id: Uuid, item: Str                

المدونات

توصيات الكتب