System Design

كشف أسرار CQRS: توسيع نطاق نظامك عبر فصل مسؤوليات الأوامر والاستعلامات

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

ما هو CQRS؟

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

يتيح هذا الفصل لجانب القراءة والكتابة امتلاك مخططات قواعد بيانات مختلفة، أو تقنيات تخزين، أو حتى بنية تحتية استضافة مختلفة، مصممة خصيصًا لتناسب حالات الاستخدام الخاصة بها.

لماذا تستخدم CQRS؟

  • تحسين الأداء: يمكنك إزالة تطبيع نموذج القراءة للاستعلامات السريعة دون التأثير على سلامة نموذج الكتابة.
  • قابلية التوسع: يمكن للتطبيقات التي تعتمد بشكل كبير على القراءة توسيع جانب القراءة بشكل مستقل عن جانب الكتابة.
  • الأمان: يمكن التحكم في عمليات الكتابة ومراجعتها بشكل صارم، بينما تبقى عمليات القراءة مفتوحة وسريعة.
  • إدارة التعقيد: في النطاقات المعقدة، يمنع فصل الاهتمامات نمط "النموذج الإلهي" (God Model) حيث تتعامل كيان واحد ضخم مع كل شيء.

نظرة عامة على البنية

في تطبيق نموذجي لـ CQRS، يبدو التدفق على النحو التالي:

  1. يُرسل أمر (Command) إلى جانب الأوامر.
  2. يقوم جانب الأوامر بالتحقق ومعالجة منطق الأعمال، مع حفظ الأحداث في مخزن الكتابة (Write Store).
  3. تُنشر الأحداث إلى ناقل الأحداث (Event Bus) أو طابور الرسائل (Message Queue).
  4. تستهلك المعالجات على جانب الاستعلامات هذه الأحداث وتحديث مخزن القراءة (Read Store) (غالبًا قاعدة بيانات NoSQL أو فهرس بحث).
  5. يُرسل استعلام (Query) إلى جانب الاستعلامات، الذي يسترجع البيانات المحسوبة مسبقًا من مخزن القراءة.

مثال على الكود

فيما يلي مثال مبسط بلغة C# يوضح فصل الأوامر عن الاستعلامات.

// 1. تعريف أمر (عملية كتابة)
public record CreateOrderCommand : ICommand<Guid>
{
    public Guid CustomerId { get; init; }
    public decimal Amount { get; init; }
}

// 2. تعريف استعلام (عملية قراءة)
public record GetOrderByIdQuery : IQuery<OrderDto>
{
    public Guid OrderId { get; init; }
}

// 3. معالج الأمر (يكتب إلى مصدر الحقيقة)
public class CreateOrderCommandHandler : ICommandHandler<CreateOrderCommand, Guid>
{
    private readonly IOrderRepository _repo;
    private readonly IEventPublisher _publisher;

    public async Task<Guid> Handle(CreateOrderCommand command, CancellationToken ct)
    {
        // منطق الأعمال والتحقق
        var order = new Order(command.CustomerId, command.Amount);
        
        // الحفظ في مخزن الكتابة (قاعدة بيانات علائقية)
        await _repo.SaveAsync(order, ct);
        
        // نشر حدث النطاق
        await _publisher.PublishAsync(new OrderCreatedEvent(order.Id), ct);
        
        return order.Id;
    }
}

// 4. معالج الاستعلام (يقراء من مخزن محسن)
public class GetOrderByIdQueryHandler : IQueryHandler<GetOrderByIdQuery, OrderDto>
{
    private readonly IOrderReadRepository _readRepo;

    public async Task<OrderDto> Handle(GetOrderByIdQuery query, CancellationToken ct)
    {
        // القراءة من NoSQL/ES/Cache
        return await _readRepo.GetByIdAsync(query.OrderId, ct);
    }
}

اعتبارات عملية

على الرغم من أن CQRS قوي، إلا أنه يضيف تعقيدًا. يجب عليك التعامل مع الاتساق النهائي (Eventual Consistency)، حيث قد يتأخر جانب القراءة عن جانب الكتابة. بالإضافة إلى ذلك، يتطلب الحفاظ على المزامنة بين نموذجي الكتابة والقراءة معالجة أحداث قوية ومراقبة دقيقة. يُوصى بالبدء بـ CQRS كنهج "فصل القراءة عن الكتابة" حيث تحتفظ بنفس قاعدة البيانات ولكن بعرضات (Views) مختلفة، قبل الانتقال إلى مخازن بيانات منفصلة تمامًا.

الخلاصة

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

Share: