जब व्यवसाय जटिल हो जाता है तो सामान्य CRUD एप्लिकेशन काम करना क्यों बंद कर देते हैं
\n\nक्या आपने कभी ऐसी स्थिति का सामना किया है, जहां एक सरल कार्य — "ऑर्डर का परिवर्तन इतिहास दिखाएं" — एक दुःस्वप्न में बदल जाता है? आप डेटाबेस में जाते हैं, लॉग देखते हैं, यह पुनर्स्थापित करने का प्रयास करते हैं कि किसने और कब स्थिति बदली। और फिर एक अनुरोध आता है: "पिछले तीन वर्षों में उपयोगकर्ता के सभी कार्यों का ऑडिट करें।" और आप समझ जाते हैं — वर्तमान आर्किटेक्चर इसे संभाल नहीं पाएगा।
\n\nनमस्ते, मेरा नाम PythonLib है, और आज हम दो शक्तिशाली पैटर्न पर चर्चा करेंगे जो ऐसी स्थितियों में बचाते हैं: CQRS (कमांड क्वेरी रिस्पॉन्सिबिलिटी सेग्रीगेशन) और इवेंट सोर्सिंग। हम TypeScript में कोड लिखेंगे जिसे आप सीधे वास्तविक प्रोजेक्ट में उपयोग कर सकते हैं।
\n\nयदि आप फिनटेक, मार्केटप्लेस, CRM या ऐसी कोई प्रणाली विकसित कर रहे हैं जहां इतिहास और ऑडिट महत्वपूर्ण है — यह लेख आपके लिए है।
\n\nसरल शब्दों में CQRS क्या है
\n\nएक पुस्तकालय की कल्पना करें। आमतौर पर एक लाइब्रेरियन किताबें देता भी है, वापस लेता भी है, और रिकॉर्ड भी रखता है। लेकिन जब आगंतुकों की संख्या बढ़ जाती है — तो लाइन लग जाती है। CQRS जिम्मेदारियों को विभाजित करने का सुझाव देता है: एक लाइब्रेरियन केवल किताबें देता है (कमांड), दूसरा केवल वापस लेता है और रिकॉर्ड रखता है (क्वेरी)।
\n\nप्रोग्रामिंग में, इसका अर्थ है पढ़ने और लिखने के लिए मॉडल को अलग करना। एक मोनोलिथिक रिपॉजिटरी के बजाय, हम बनाते हैं:
\n\n- \n
- कमांड मॉडल — डेटा बदलने के लिए (क्रिएट, अपडेट, डिलीट) \n
- क्वेरी मॉडल — डेटा पढ़ने के लिए (रीड) \n
क्या यह जटिल लगता है? हाँ, शुरुआत में। लेकिन जब सिस्टम बढ़ता है, तो आपको लचीलापन मिलता है: आप लिखने से अलग पढ़ने को ऑप्टिमाइज़ कर सकते हैं, विभिन्न डेटाबेस, कैश का उपयोग कर सकते हैं।
\n\nTypeScript में उदाहरण: इवेंट सोर्सिंग के बिना सरल CQRS
\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