परिचय: आपका एप्लिकेशन उसका डेटाबेस क्यों नहीं है
\n\nकल्पना करें कि आप एक घर बना रहे हैं। नींव, दीवारें, छत — सब कुछ सोच-समझकर बनाया गया है। लेकिन एक साल बाद आप वायरिंग बदलने का फैसला करते हैं। और तब पता चलता है कि तार कंक्रीट में दबे हुए हैं, और सॉकेट सरिया से वेल्डेड हैं। एक स्विच बदलने के लिए आधा घर गिराना पड़ता है।
\n\nलगभग ऐसा ही एक क्लासिक जावा एप्लिकेशन दिखता है, जहाँ बिजनेस लॉजिक फ्रेमवर्क, डेटाबेस या REST-कंट्रोलर के साथ कसकर जुड़ा होता है। आप PostgreSQL से MongoDB पर स्विच करना चाहते हैं? Spring MVC से JAX-RS पर कंट्रोलर फिर से लिखना चाहते हैं? रिफैक्टरिंग के नरक में आपका स्वागत है।
\n\nसमाधान है — हेक्सागोनल आर्किटेक्चर (जिसे "पोर्ट और एडेप्टर" भी कहा जाता है, या एलिस्टेयर कोबर्न की व्याख्या में "क्लीन आर्किटेक्चर")। यह कोई चांदी की गोली नहीं है, लेकिन यह आपके एप्लिकेशन को लचीला, परीक्षण योग्य और बाहरी दुनिया से स्वतंत्र बनाने का एक तरीका है।
\n\nआज हम जावा में हेक्सागोनल आर्किटेक्चर को कोड के वास्तविक उदाहरणों के साथ समझेंगे, बिना फालतू बातों और अमूर्त चर्चाओं के। चलिए शुरू करते हैं।
\n\nहेक्सागोनल आर्किटेक्चर (पोर्ट और एडेप्टर) क्या है?
\n\nसार सरल है: आपका एप्लिकेशन एक कोर (core) है जिसमें बिजनेस लॉजिक होता है। कोर के चारों ओर एक "षट्भुज" (वास्तव में मनमानी संख्या में भुजाएँ) होता है, जिसके माध्यम से बाहरी दुनिया कोर के साथ संवाद करती है।
\n\nप्रत्येक भुजा का अपना पोर्ट (इंटरफ़ेस) होता है। और एडेप्टर किसी विशिष्ट तकनीक के लिए पोर्ट का कार्यान्वयन है। उदाहरण के लिए:
\n- \n
- पोर्ट "UserRepository" — एडेप्टर "JpaUserRepository" या "MongoUserRepository" \n
- पोर्ट "SendNotification" — एडेप्टर "EmailSender" या "SmsSender" \n
मुख्य नियम: कोर बाहरी लाइब्रेरी, डेटाबेस, वेब फ्रेमवर्क के बारे में कुछ नहीं जानता। वह केवल अपने पोर्ट (इंटरफ़ेस) के बारे में जानता है। यह कई लाभ प्रदान करता है:
\n- \n
- इन्फ्रास्ट्रक्चर से स्वतंत्रता — लॉजिक को छुए बिना DB या फ्रेमवर्क बदलें। \n
- परीक्षण योग्यता — बिजनेस लॉजिक को Spring कॉन्टेक्स्ट या डेटाबेस शुरू किए बिना परीक्षण किया जा सकता है। \n
- पठनीयता — कोर कोड डोमेन की कहानी बताता है, तकनीकी विवरण नहीं। \n
प्रोजेक्ट संरचना: कोड को कैसे व्यवस्थित करें
\n\nपैकेज संरचना का कोई एक सही तरीका नहीं है, लेकिन मैं DDD (Domain-Driven Design) और हेक्सागोनल सिद्धांतों पर आधारित एक सिद्ध विकल्प प्रस्तुत करता हूँ:
\n\ncom.example.app\n├── domain\n│ ├── model // एंटिटी, Value Objects\n│ ├── port // पोर्ट इंटरफ़ेस (inbound/outbound)\n│ └── service // बिजनेस सर्विस (use cases)\n├── adapter\n│ ├── inbound\n│ │ ├── rest // REST-कंट्रोलर (इनकमिंग एडेप्टर)\n│ │ └── messaging // Kafka/RabbitMQ सुनने वाले\n│ └── outbound\n│ ├── persistence // JPA, JDBC, MongoDB रिपॉजिटरी\n│ └── client // HTTP-क्लाइंट, बाहरी API\n└── config // Spring कॉन्फ़िगरेशन (यदि उपयोग कर रहे हैं)\n\n\nध्यान दें: domain adapter से कुछ भी इम्पोर्ट नहीं करता है। यह महत्वपूर्ण है। यदि आप domain में @Entity या @RestController देखते हैं — तो आर्किटेक्चर टूट गया है।
उदाहरण: ऑर्डर प्रबंधन प्रणाली
\n\nआइए एक सरल प्रणाली डिज़ाइन करें: उपयोगकर्ता एक ऑर्डर बना सकता है, और सिस्टम गोदाम में उत्पाद की उपलब्धता की जाँच करता है और एक सूचना भेजता है।
\n\nचरण 1. डोमेन मॉडल (domain/model)
\n\n// Order.java\npublic class Order {\n private final OrderId id;\n private final List<OrderItem> items;\n private OrderStatus status;\n\n public Order(List<OrderItem> items) {\n this.id = new OrderId(UUID.randomUUID().toString());\n this.items = Collections.unmodifiableList(items);\n this.status = OrderStatus.PENDING;\n }\n\n public void confirm() {\n if (status != OrderStatus.PENDING) {\n throw new IllegalStateException("Only PENDING orders can be confirmed");\n }\n this.status = Ord