CQRS और Event Sourcing PHP में: जटिल सिस्टम के लिए आर्किटेक्चर

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

जब सामान्य CRUD काम करना बंद कर दे: CQRS और Event Sourcing से परिचय

\n\n

क्या आपने कभी ऐसी स्थिति का सामना किया है जब आपका PHP एप्लिकेशन बढ़ता गया, बढ़ता गया... और एक दिन आपका सामान्य UserController 2000 लाइनों का राक्षस बन गया? परिचित लग रहा है? बग बढ़ते हैं, परफॉरमेंस गिरती है, और नई सुविधा जोड़ना पूरी टीम के लिए सिरदर्द बन जाता है। बधाई हो, आप उस मुकाम पर पहुँच गए हैं जहाँ गंभीर आर्किटेक्चरल पैटर्न अपनाने का समय आ गया है।

\n\n

आज हम दो सबसे शक्तिशाली टूल्स के बारे में बात करेंगे — CQRS (Command Query Responsibility Segregation) और Event Sourcing। जटिल शब्दों से डरने की ज़रूरत नहीं है। असल में, ये सिर्फ़ डेटा पढ़ने और लिखने को अलग करने और मौजूदा स्थिति के बजाय बदलावों का इतिहास स्टोर करने के सुरुचिपूर्ण तरीके हैं।

\n\n

यह लेख उन PHP डेवलपर्स के लिए है जिन्होंने OOP की बुनियादी बातें सीख ली हैं और अपने प्रोजेक्ट्स को अगले स्तर पर ले जाना चाहते हैं। आप सीखेंगे कि CQRS और Event Sourcing को व्यवहार में कैसे लागू करें, असली कोड देखेंगे, और समझेंगे कि ये पैटर्न कब वास्तव में ज़रूरी हैं और कब इनसे बचना चाहिए।

\n\n

CQRS क्या है? कमांड और क्वेरी को अलग करना

\n\n

कल्पना करें कि आपका कोड एक रेस्टोरेंट है। आमतौर पर, वेटर (कंट्रोलर) ऑर्डर लेता है (लिखने का अनुरोध), किचन (डेटाबेस) में जाता है, खाना पकाता है (डेटा प्रोसेस करता है) और तुरंत परोसता है (जवाब देता है)। जब तक रेस्टोरेंट में 10 लोग हैं, सब ठीक है। लेकिन जब 1000 मेहमान आते हैं, तो किचन थकने लगता है।

\n\n

CQRS (Command Query Responsibility Segregation) जिम्मेदारी को अलग करने का सुझाव देता है: कुछ शेफ (कमांड) सिर्फ़ पकाते हैं, दूसरे वेटर (क्वेरी) सिर्फ़ परोसते हैं। तकनीकी रूप से, इसका मतलब है कि आप डेटा पढ़ने और लिखने के लिए अलग-अलग मॉडल का उपयोग करते हैं।

\n\n

PHP में CQRS की बुनियादी अमल

\n\n

आइए देखें कि कोड में यह कैसा दिखता है। हम फ्रेमवर्क के बिना एक सरल उदाहरण से शुरू करेंगे ताकि सार समझ में आए।

\n\n
// कमांड — स्थिति बदलने के लिए डेटा वाला DTO\nclass CreateUserCommand {\n    public function __construct(\n        public readonly string $email,\n        public readonly string $name\n    ) {}\n}\n\n// कमांड हैंडलर — यहाँ बिज़नेस लॉजिक होता है\nclass CreateUserHandler {\n    public function __construct(\n        private UserRepository $repository,\n        private EventDispatcher $dispatcher\n    ) {}\n\n    public function handle(CreateUserCommand $command): void {\n        $user = new User($command->email, $command->name);\n        $this->repository->save($user);\n        $this->dispatcher->dispatch(new UserCreatedEvent($user->getId()));\n    }\n}\n\n// क्वेरी — डेटा प्राप्त करने के लिए DTO\nclass GetUserQuery {\n    public function __construct(\n        public readonly int $userId\n    ) {}\n}\n\n// क्वेरी हैंडलर — सिर्फ़ पढ़ना, कोई बदलाव नहीं\nclass GetUserHandler {\n    public function __construct(\n        private UserReadRepository $readRepository\n    ) {}\n\n    public function handle(GetUserQuery $query): ?UserDTO {\n        return $this->readRepository->findById($query->userId);\n    }\n}
\n\n

ध्यान दें: हमारे पास दो अलग-अलग रिपॉजिटरी हैं। UserRepository लिखने के लिए जिम्मेदार है (जटिल बिज़नेस लॉजिक, वैलिडेशन का उपयोग कर सकता है), जबकि UserReadRepository पढ़ने के लिए (एक सरल SQL क्वेरी या डीनॉर्मलाइज़्ड टेबल भी हो सकता है)।

\n\n

कमांड बस (Command Bus)

\n\n

असली प्रोजेक्ट्स में, कमांड और क्वेरी एक बस के माध्यम से भेजे जाते हैं — एक केंद्रीय डिस्पैचर जो खुद सही हैंडलर ढूंढता है।

\n\n
// Command Bus की सरल अमल\nclass SimpleCommandBus {\n    public function __construct(\n        private array $handlers = []\n    ) {}\n\n    public function registerHandler(string $commandClass, object $handler): void {\n        $this->handlers[$commandClass] = $handler;\n    }\n\n    public function dispatch(object $command): void {\n        $handler = $this->handlers[$co                

ब्लॉग

पुस्तक अनुशंसाएँ