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

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

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

ابدأ الدورة

عندما يتوقف CRUD العادي عن العمل: مقدمة إلى CQRS و Event Sourcing

\n\n

تخيل أنك تقوم بكتابة نظام لبنك. كل ثانية، مئات المستخدمين يتحققون من الرصيد، ويحولون الأموال، ويفتحون الودائع. تطبيقك على C# يعمل على Entity Framework القياسي مع عمليات CRUD النموذجية. وفجأة - تتعطل قاعدة البيانات. لماذا؟ لأن تقريرًا ثقيلًا واحدًا قام بحظر الجدول أثناء كتابة المعاملة. هل هذا مألوف؟

\n\n

مطورو الأنظمة المعقدة على C# عاجلاً أم آجلاً يصطدمون بسقف البنية الكلاسيكية. تأتي المساعدة من نمطين قويين: CQRS (فصل مسؤولية الأوامر والاستعلامات) و Event Sourcing. دعنا نحللهما عمليًا - بأمثلة حية، وليس بنظرية جافة.

\n\n

ما هو CQRS ولماذا هو ليس مجرد "تقسيم"؟

\n\n

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

\n\n

مشكلة النهج الكلاسيكي

\n

لنفترض أن لديك UserRepository مع طريقة GetUserById. عندما تقرأ المستخدم لعرضه على الشاشة، تحتاج إلى 3 حقول. عندما تقوم بالتحديث - 10 حقول وتحقق. ونتيجة لذلك، يتحول النموذج الواحد إلى وحش يحتوي على حقول null ومخططات تعيين مربكة.

\n\n

في CQRS، تقوم بفصل المسؤولية:

\n
    \n
  • Command - ينفذ إجراءً (مثل CreateOrderCommand)
  • \n
  • Query - يعيد البيانات (مثل GetOrderQuery)
  • \n
  • Read Model - DTO مسطح، مُحسَّن لشاشة معينة
  • \n
  • Write Model - نموذج مجال غني بمنطق الأعمال
  • \n
\n\n

كتابة CQRS على C# باستخدام MediatR

\n

الطريقة الأكثر شيوعًا لتنفيذ CQRS في C# هي استخدام مكتبة MediatR. وهي تنفذ نمط Mediator وتتناسب تمامًا مع مفهوم الأوامر والاستعلامات.

\n\n

الخطوة 1: تعريف الأمر

\n
public record CreateOrderCommand(\n    Guid CustomerId,\n    List<OrderItemDto> Items,\n    string ShippingAddress\n) : IRequest<Guid>;  // نعيد معرف الطلب الذي تم إنشاؤه
\n\n

الخطوة 2: معالج الأمر

\n
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, Guid>\n{\n    private readonly IOrderRepository _orderRepository;\n    private readonly IUnitOfWork _unitOfWork;\n\n    public CreateOrderCommandHandler(IOrderRepository orderRepository, IUnitOfWork unitOfWork)\n    {\n        _orderRepository = orderRepository;\n        _unitOfWork = unitOfWork;\n    }\n\n    public async Task<Guid> Handle(CreateOrderCommand request, CancellationToken cancellationToken)\n    {\n        // منطق الأعمال: التحقق، المصادقة، الإنشاء\n        var order = new Order(request.CustomerId, request.ShippingAddress);\n        \n        foreach (var item in request.Items)\n        {\n            order.AddItem(item.ProductId, item.Quantity, item.Price);\n        }\n\n        await _orderRepository.AddAsync(order);\n        await _unitOfWork.SaveChangesAsync(cancellationToken);\n        \n        return order.Id;\n    }\n}
\n\n

الخطوة 3: تعريف الاستعلام

\n
public record GetOrderSummaryQuery(Guid OrderId) : IRequest<OrderSummaryDto>;
\n\n

الخطوة 4: معالج الاستعلام (يقرأ من قاعدة بيانات منفصلة!)

\n
public class GetOrderSummaryQueryHandler : IRequestHandler<GetOrderSummaryQuery, OrderSummaryDto>\n{\n    private readonly IReadDbContext _readDbContext; // سياق منفصل للقراءة\n\n    public GetOrderSummaryQueryHandler(IReadDbContext read                

المدونات

توصيات الكتب