आपका कोड एक किला है, सेवइयां नहीं
\n\nक्या आपने कभी Laravel से Symfony या MySQL से PostgreSQL पर प्रोजेक्ट बदला है? अगर हाँ, तो आप उस दर्द को जानते हैं: SQL क्वेरीज़ से भरे कंट्रोलर, HTTP रिक्वेस्ट के बारे में सब कुछ जानने वाले मॉडल, और बिज़नेस लॉजिक जो फ्रेमवर्क बदलने पर मर जाता है। परिचित लग रहा है?
\n\nPHP में Hexagonal Architecture (जिसे Ports & Adapters भी कहा जाता है) की दुनिया में आपका स्वागत है। यह कोई फैशनेबल buzzword नहीं, बल्कि उन प्रोजेक्ट्स के लिए जीवन रेखा है जो लंबे समय तक चलना चाहते हैं। इस लेख में, हम जानेंगे कि PHP एप्लिकेशन कैसे बनाएं जो बदलाव से न डरे: डेटाबेस बदलना, दूसरे कैश पर जाना या नया API — ये सब सिर्फ एक एडॉप्टर बदलने का मामला होगा, आधा कोड दोबारा लिखने का नहीं।
\n\nHexagonal Architecture वास्तव में क्या है?
\n\nकल्पना करें कि आपका एप्लिकेशन एक कोर (core) है जिसे नहीं पता कि डेटा कहाँ से आ रहा है: वेब, कंसोल, Telegram बॉट या टेस्ट से। इस कोर के चारों ओर पोर्ट्स (इंटरफेस) हैं, और बाहर एडॉप्टर (इम्प्लीमेंटेशन) हैं। PHP में Hexagonal Architecture बिज़नेस लॉजिक को इन्फ्रास्ट्रक्चर कोड से अलग करता है। आप नियम एक बार लिखते हैं और उन्हें हर जगह उपयोग करते हैं।
\n\nमुख्य सिद्धांत:
\n- \n
- डिपेंडेंसी इनवर्जन — उच्च स्तर का कोड निम्न स्तर पर निर्भर नहीं करता। सब कुछ उल्टा है। \n
- बिज़नेस लॉजिक अलग — कोई भी फ्रेमवर्क आपके Use Cases में प्रवेश नहीं करता। \n
- टेस्ट करने की क्षमता — आप बिना डेटाबेस और HTTP के कोर को टेस्ट कर सकते हैं। \n
रॉबर्ट मार्टिन के Clean Architecture के विपरीत, Hexagonal अधिक लचीला है: यह लेयर्स की संख्या निर्धारित नहीं करता, बल्कि सिर्फ कहता है: "अंदर डोमेन है, बाहर इन्फ्रास्ट्रक्चर है, उन्हें इंटरफेस के माध्यम से जोड़ें।"
\n\nप्रोजेक्ट स्ट्रक्चर: अव्यवस्था से व्यवस्था तक
\n\nचलिए सीधे काम पर आते हैं। PHP में Hexagonal Architecture के लिए एक सामान्य फोल्डर संरचना:
\n\nsrc/\n├── Domain/\n│ ├── Entity/\n│ │ └── Order.php\n│ ├── Repository/\n│ │ └── OrderRepositoryInterface.php\n│ └── Service/\n│ └── OrderService.php\n├── Application/\n│ ├── UseCase/\n│ │ └── CreateOrderUseCase.php\n│ └── Port/\n│ ├── Input/\n│ │ └── CreateOrderInputPort.php\n│ └── Output/\n│ └── OrderOutputPort.php\n├── Infrastructure/\n│ ├── Persistence/\n│ │ └── DoctrineOrderRepository.php\n│ └── Http/\n│ └── OrderController.php\n└── Framework/\n └── Symfony/\n └── Kernel.php\n\nध्यान दिया? Domain और Application को Doctrine, Symfony या MySQL के बारे में पता नहीं है। वे केवल इंटरफेस के साथ काम करते हैं। एडॉप्टर Infrastructure में रहते हैं और Dependency Injection के माध्यम से जुड़ते हैं।
चरण 1: कोर को परिभाषित करें — Domain
\n\nऑर्डर एंटिटी से शुरू करते हैं। कोई ORM एनोटेशन नहीं, कोई सेटर नहीं जो इनवेरिएंट्स को तोड़ते हैं:
\n\n<?php\n// src/Domain/Entity/Order.php\nnamespace App\\Domain\\Entity;\n\nclass Order\n{\n public function __construct(\n private readonly string $id,\n private readonly string $productName,\n private float $totalAmount,\n private string $status = 'pending'\n ) {}\n\n public function approve(): void\n {\n if ($this->status !== 'pending') {\n throw new \\DomainException('Only pending orders can be approved.');\n }\n $this->status = 'approved';\n }\n\n public function getId(): string { return $this->id; }\n public function getProductName(): string { return $this->productName; }\n public function getTotalAmount(): float { return $this->totalAmount; }\n public function getStatus(): string { return $this->status; }\n}\n\nऔर यहाँ रिपॉजिटरी के लिए पोर्ट (इंटरफेस) है। ध्यान दें: इसे Doctrine या PDO के बारे में कुछ नहीं पता:
\n\n<?php\n// src/Domain/Repository/OrderRepositoryInterface.php\nnamespace App\\Domain\\Repository;\n\nuse App\\