CQRS و Event Sourcing في لغة C++: بنية للأنظمة المعقدة

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

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

ابدأ الدورة

عندما يتوقف CRUD العادي عن العمل: التعرف على CQRS و Event Sourcing بلغة C++



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



هذان النمطان ليسا مجرد كلمات رنانة من عالم الخدمات المصغرة. إنهما أدوات قوية تسمح ببناء أنظمة قابلة للتوسع، وقابلة للتدقيق، ومرنة. ونعم، يتناسبان تمامًا مع لغة C++ - اللغة التي تعتبر فيها الأداء والتحكم في الذاكرة أمرين حاسمين.



في هذه المقالة، لن نلقي محاضرات مملة. سنشرح كيفية تنفيذ CQRS و Event Sourcing بلغة C++ خالصة دون سحر الأطر العمل، مع أمثلة حقيقية وقوالب جاهزة للاستخدام.



CQRS: فصل القراءة والكتابة



في البنية الكلاسيكية CRUD (إنشاء، قراءة، تحديث، حذف)، نستخدم نفس النموذج لكل من القراءة والكتابة. هذا مناسب طالما أن النظام لا يبدأ في النمو. تظهر المشاكل عندما:



  • يكون الكائن في الذاكرة معقدًا جدًا بحيث لا يمكن تسلسله بسرعة للقراءة.
  • يتطلب منطق الأعمال للكتابة التحقق من الصحة مما يبطئ القراءة.
  • تريد أجزاء مختلفة من النظام رؤية نفس البيانات بطرق مختلفة.


CQRS يقدم حلاً بسيطًا ولكنه عبقري: تقسيم النموذج إلى جزأين - Command (أمر) لتغيير الحالة و Query (استعلام) للقراءة. يمكنهما استخدام مخازن مختلفة، وهياكل بيانات مختلفة، وحتى يتم تنفيذهما على أجهزة مختلفة.



التنفيذ الأساسي لـ CQRS بلغة C++



لنبدأ بشيء بسيط. تخيل أننا نكتب نظامًا لإدارة الطلبات. بدلاً من تخزين كل شيء في فئة واحدة Order، سنقوم بفصل المسؤوليات.



أولاً، سنحدد واجهات الأوامر والاستعلامات:



#include <memory>#include <vector>#include <string>

// الفئة الأساسية لجميع الأوامرclass Command {public: virtual ~Command() = default; virtual void execute() = 0;};

// الفئة الأساسية لجميع الاستعلاماتtemplate <typename TResult>class Query {public: virtual ~Query() = default; virtual TResult execute() = 0;};


الآن سننشئ أمرًا محددًا لإضافة عنصر إلى الطلب:



class AddItemCommand : public Command {private:    std::string orderId_;    std::string productId_;    int quantity_;    // مرجع إلى المستودع (أو ناقل الأوامر)    OrderRepository& repository_;

public: AddItemCommand(const std::string& orderId, const std::string& productId, int quantity, OrderRepository& repo) : orderId_(orderId), productId_(productId), quantity_(quantity), repository_(repo) {}

void execute() override { // منطق الأعمال: التحقق من وجود العنصر في المخزون if (quantity_ <= 0) { throw std::invalid_argument("Quantity must be positive"); } // استدعاء خدمة المجال أو المُجمّع auto& order = repository_.getById(orderId_); order.addItem(productId_, quantity_); repository_.save(order); }};


وهذا ما يبدو عليه الاستعلام للحصول على ملخص الطلب:



class OrderSummaryQuery : public Query<OrderSummary> {private:    std::string orderId_;    ReadModelDatabase& db_;

public: OrderSummaryQuery(const std::string& orderId, ReadModelDatabase& db) : orderId_(orderId), db_(db) {}

OrderSummary execute() override { // القراءة من قاعدة بيانات محسّنة للقراءة return db_.getOrderSummary(ord

المدونات

توصيات الكتب