لماذا تتوقف تطبيقات CRUD العادية عن العمل عندما يصبح العمل معقدًا
\n\nهل سبق لك أن واجهت موقفًا تتحول فيه مهمة بسيطة - "إظهار تاريخ تغييرات الطلب" - إلى جحيم؟ تتصفح قاعدة البيانات، تنظر إلى السجلات، تحاول استعادة من ومتى غيّر الحالة. ثم يأتي طلب: "قم بتدقيق جميع إجراءات المستخدم خلال السنوات الثلاث الماضية". وتدرك - أن البنية الحالية لن تتحمل ذلك.
\n\nمرحبًا، اسمي PythonLib، واليوم سنشرح نمطين قويين ينقذان في مثل هذه المواقف: CQRS (فصل مسؤوليات الأوامر والاستعلامات) و Event Sourcing. سنكتب كودًا بلغة TypeScript يمكن استخدامه فورًا في مشروع حقيقي.
\n\nإذا كنت تطور أنظمة fintech، أو الأسواق الإلكترونية، أو CRM، أو أي أنظمة تكون فيها التاريخ والتدقيق مهمين - فهذه المقالة لك.
\n\nما هو CQRS بكلمات بسيطة
\n\nتخيل مكتبة. عادةً، أمين مكتبة واحد يقوم بإعارة الكتب، واستلامها، وحفظ السجلات. ولكن عندما يزداد عدد الزوار - يتشكل طابور. يقترح CQRS فصل المسؤوليات: أمين مكتبة واحد فقط يعير (أوامر)، والثاني فقط يستلم ويسجل (استعلامات).
\n\nفي البرمجة، هذا يعني فصل نماذج القراءة والكتابة. بدلاً من مستودع واحد متكامل، ننشئ:
\n\n- \n
- نموذج الأوامر (Command Model) - لتغيير البيانات (إنشاء، تحديث، حذف) \n
- نموذج الاستعلامات (Query Model) - لقراءة البيانات (قراءة) \n
يبدو هذا تعقيدًا؟ نعم، في البداية. ولكن عندما ينمو النظام، تحصل على مرونة: يمكن تحسين القراءة بشكل منفصل عن الكتابة، واستخدام قواعد بيانات مختلفة، والتخزين المؤقت.
\n\nمثال باستخدام TypeScript: CQRS بسيط بدون Event Sourcing
\n\nلنفترض أن لدينا نظام إدارة طلبات. أولاً، نحدد الكيانات:
\n\n// domain/order.ts\nexport interface Order {\n id: string;\n userId: string;\n items: OrderItem[];\n status: 'pending' | 'confirmed' | 'shipped' | 'cancelled';\n total: number;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface OrderItem {\n productId: string;\n quantity: number;\n price: number;\n}\n\n\nالآن ننشئ أمرًا واستعلامًا:
\n\n// application/commands/create-order.command.ts\nexport class CreateOrderCommand {\n constructor(\n public readonly userId: string,\n public readonly items: OrderItem[]\n ) {}\n}\n\n// application/commands/create-order.handler.ts\nimport { CommandHandler, ICommandHandler } from '@nestjs/cqrs';\nimport { OrderRepository } from '../repositories/order.repository';\n\n@CommandHandler(CreateOrderCommand)\nexport class CreateOrderHandler implements ICommandHandler<CreateOrderCommand> {\n constructor(private readonly orderRepo: OrderRepository) {}\n\n async execute(command: CreateOrderCommand): Promise<string> {\n const order: Order = {\n id: uuid(),\n userId: command.userId,\n items: command.items,\n status: 'pending',\n total: command.items.reduce((sum, item) => sum + item.price * item.quantity, 0),\n createdAt: new Date(),\n updatedAt: new Date()\n };\n await this.orderRepo.save(order);\n return order.id;\n }\n}\n\n\nوللقراءة - معالج منفصل:
\n\n// application/queries/get-order.query.ts\nexport class GetOrderQuery {\n constructor(public readonly orderId: string) {}\n}\n\n// application/queries/get-order.handler.ts\nimport { QueryHandler, IQueryHandler } from '@nestjs/cqrs';\nimport { OrderReadRepository } from '../repositories/order-read.repository';\n\n@QueryHandler(GetOrderQuery)\nexport class GetOrderHandler implements IQueryHandler<GetOrderQuery> {\n constructor(private readonly readRepo: OrderReadRepository) {}\n\n async execute(query: GetOrderQuery): Promise<Order | null> {\n return this.readRepo.findById(query.orderId);\n }\n}\n