CQRS और Event Sourcing "केवल चुनिंदा लोगों के लिए" क्यों नहीं हैं?
\n\nजब हम "CQRS" या "Event Sourcing" जैसे शब्द सुनते हैं, तो कई जावास्क्रिप्ट डेवलपर्स Java या C# पर एंटरप्राइज़ आर्किटेक्चर की दुनिया की कल्पना करते हैं। जटिल योजनाएँ, अंतहीन घटनाएँ, अजीब स्टोरेज... लेकिन क्या होगा अगर मैं आपसे कहूँ कि इन पैटर्न का उपयोग सामान्य Node.js एप्लिकेशन में भी किया जा सकता है (और किया जाना चाहिए!)?
\n\nइस लेख में, हम समझेंगे कि CQRS और Event Sourcing वास्तविक समस्याओं को हल करने में कैसे मदद करते हैं: डेटा विरोध, जटिल लॉगिंग, स्केलिंग और डिबगिंग। और यह सब — शुद्ध जावास्क्रिप्ट में, बिना किसी जादू या जटिल फ्रेमवर्क के।
\n\nCQRS (Command Query Responsibility Segregation) क्या है?
\n\nकल्पना करें कि आपका एप्लिकेशन एक रेस्तरां है। वेटर (क्वेरीज़) और शेफ (कमांड) एक ही कमरे में काम करते हैं। देर-सबेर वे एक-दूसरे के काम में बाधा डालने लगेंगे। CQRS इन क्षेत्रों को अलग करने का सुझाव देता है: वेटर हॉल में काम करते हैं (पढ़ना), और शेफ रसोई में (लिखना)।
\n\nCQRS — यह संचालन को दो चैनलों में विभाजित करने का सिद्धांत है:
\n- \n
- कमांड (Commands) — सिस्टम की स्थिति बदलते हैं (Create, Update, Delete) \n
- क्वेरीज़ (Queries) — केवल डेटा पढ़ते हैं (Read) \n
क्लासिकल REST API में, हम आमतौर पर पढ़ने और लिखने के लिए एक ही ऑब्जेक्ट का उपयोग करते हैं। उदाहरण के लिए, UserController में GET /users/:id और POST /users विधियाँ होती हैं। CQRS उन्हें अलग-अलग परतों में रखने का सुझाव देता है।
जावास्क्रिप्ट में CQRS का सरल उदाहरण
\n\n// कमांड (स्थिति बदलना)\nclass CreateUserCommand {\n constructor(name, email) {\n this.name = name;\n this.email = email;\n }\n}\n\nclass UserCommandHandler {\n async handle(command) {\n // वैलिडेशन, बिज़नेस लॉजिक, सेव करना\n return await db.users.insert({\n name: command.name,\n email: command.email,\n created_at: new Date()\n });\n }\n}\n\n// क्वेरी (केवल पढ़ना)\nclass GetUserQuery {\n constructor(userId) {\n this.userId = userId;\n }\n}\n\nclass UserQueryHandler {\n async handle(query) {\n // केवल पढ़ना, कोई बदलाव नहीं!\n return await db.users.findOne({ _id: query.userId });\n }\n}\n\nऐसा लगता है कि यह कोड को जटिल बनाता है? हाँ, पहली नज़र में। लेकिन जब आपका एप्लिकेशन 50+ एंडपॉइंट तक बढ़ जाता है, तो कमांड और क्वेरी का पृथक्करण आपको इससे बचाता है:
\n- \n
- GET अनुरोधों में डेटा के आकस्मिक परिवर्तन से \n
- कैशिंग समस्याओं से (क्वेरीज़ को कैश करना आसान है, कमांड को नहीं) \n
- कई डेवलपर्स के समानांतर काम करने पर विरोध से \n
Event Sourcing: हर क्रिया को याद रखना
\n\nअब कल्पना करें कि उपयोगकर्ता की वर्तमान स्थिति को संग्रहीत करने के बजाय, आप उसके साथ हुई सभी घटनाओं को संग्रहीत करते हैं। "इवान, 25 वर्ष, मॉस्को" नहीं, बल्कि एक अनुक्रम:
\n- \n
- "उपयोगकर्ता बनाया गया: इवान" \n
- "उपयोगकर्ता ने आयु बदलकर 25 कर दी" \n
- "उपयोगकर्ता मॉस्को चला गया" \n
Event Sourcing — यह एक पैटर्न है जहाँ स्थिति में प्रत्येक परिवर्तन एक अपरिवर्तनीय घटना के रूप में सहेजा जाता है। वर्तमान स्थिति की गणना सभी घटनाओं को "पुनः चलाकर" की जाती है।
\n\nजावास्क्रिप्ट में Event Store का कार्यान्वयन
\n\nclass EventStore {\n constructor() {\n this.events = [];\n }\n\n append(aggregateId, event) {\n this.events.push({\n aggregateId,\n event,\n timestamp: new Date()\n });\n }\n\n getEvents(aggregateId) {\n return this.events.filter(e => e.aggregateId === aggregateId);\n }\n}\n\n// घटनाओं से स्थिति का पुनर्निर्माण\nfunction rebuildState(events) {\n return events.reduce((state, event) => {\n switch(event.event.type) {\n case 'USER_CREATED':\n return { ...state, name: event.event.name, age: 0 };\n case