Kotlin में हेक्सागोनल आर्किटेक्चर: जब कोड स्वतंत्र रूप से सांस लेता है
\n\nकल्पना करें कि आप एक घर बना रहे हैं। आप सीधे नींव डाल सकते हैं, दीवारें खड़ी कर सकते हैं और खिड़कियाँ लगा सकते हैं। या फिर पहले एक ढांचा डिज़ाइन कर सकते हैं, संचार लाइनें बिछा सकते हैं और उसके बाद ही फिनिशिंग कर सकते हैं। प्रोग्रामिंग की दुनिया में, दूसरे दृष्टिकोण को हेक्सागोनल आर्किटेक्चर (जिसे Ports & Adapters भी कहा जाता है) कहा जाता है। और Kotlin अपनी संक्षिप्तता और शक्तिशाली टाइप सिस्टम के साथ इस अवधारणा को जीवन में उतारने के लिए एक आदर्श भाषा है।
\n\nयह लेख कोई उबाऊ पाठ्यपुस्तक नहीं है। यह एक व्यावहारिक मार्गदर्शिका है जो दिखाएगी कि कैसे Kotlin में हेक्सागोनल आर्किटेक्चर आपके कोड को एक लचीली, परीक्षण योग्य और बाहरी सेवाओं से स्वतंत्र प्रणाली में बदल देता है। हम शुरू से एक उदाहरण तैयार करेंगे, वास्तविक कोड लिखेंगे और देखेंगे कि यह व्यवहार में कैसे काम करता है।
\n\nहेक्सागोनल आर्किटेक्चर क्या है और आपको इसकी आवश्यकता क्यों है?
\n\nएलिस्टेयर कॉकबर्न, जिन्होंने इस शब्द को गढ़ा, ने एप्लिकेशन की तुलना एक षट्भुज (हेक्सागोन) से की। केंद्र में आपका व्यावसायिक तर्क (बिजनेस लॉजिक) है। किनारों पर पोर्ट (इंटरफ़ेस) हैं, जिनके माध्यम से यह बाहरी दुनिया से संवाद करता है। एडेप्टर विशिष्ट तकनीकों के लिए इन इंटरफ़ेस के कार्यान्वयन हैं: डेटाबेस, वेब फ्रेमवर्क, मैसेज क्यू।
\n\nमुख्य विचार: व्यावसायिक तर्क को यह नहीं पता होता कि डेटा कैसे संग्रहीत किया जाता है या किस फ्रेमवर्क का उपयोग किया जाता है। यह केवल पोर्ट के तरीकों को कॉल करता है। PostgreSQL को MongoDB से बदलना चाहते हैं? बस एक नया एडेप्टर लिखें। व्यावसायिक तर्क को बदलाव का पता भी नहीं चलेगा।
\n\nKotlin में, यह डिफ़ॉल्ट कार्यान्वयन वाले इंटरफ़ेस, sealed-क्लास और कार्यात्मक शैली के कारण विशेष रूप से सुविधाजनक है। चलिए काम पर आते हैं।
\n\nसिस्टम डिज़ाइन करना: एक ऑनलाइन स्टोर का उदाहरण
\n\nमान लीजिए कि हम ऑर्डर प्रबंधन के लिए एक माइक्रोसर्विस लिख रहे हैं। केंद्र में OrderService है, जो ऑर्डर बनाता है और उत्पादों की उपलब्धता की जाँच करता है। बाहरी दुनिया में HTTP कंट्रोलर और डेटाबेस है।
प्रोजेक्ट की संरचना इस प्रकार है:
\n\nsrc/\n├── domain/ # कोर (व्यावसायिक तर्क)\n│ ├── model/\n│ ├── port/\n│ └── service/\n├── adapter/\n│ ├── input/ # इनकमिंग एडेप्टर (कंट्रोलर)\n│ └── output/ # आउटगोइंग एडेप्टर (रिपॉजिटरी)\n└── Application.kt # प्रवेश बिंदु\n\n\nचरण 1. कोर: मॉडल और पोर्ट
\n\nऑर्डर मॉडल से शुरू करते हैं। Kotlin में यह एक data class होगा:
// domain/model/Order.kt\ndata class Order(\n val id: String,\n val customerId: String,\n val items: List<OrderItem>,\n val status: OrderStatus\n)\n\nenum class OrderStatus {\n PENDING, CONFIRMED, SHIPPED, CANCELLED\n}\n\ndata class OrderItem(\n val productId: String,\n val quantity: Int\n)\n\n\nअब पोर्ट परिभाषित करते हैं - इंटरफ़ेस जिसके माध्यम से कोर बाहरी दुनिया से संवाद करता है। हमारे पास दो पोर्ट होंगे: एक उत्पादों के बारे में डेटा प्राप्त करने के लिए, दूसरा ऑर्डर सहेजने के लिए।
\n\n// domain/port/ProductRepository.kt\ninterface ProductRepository {\n fun checkAvailability(productId: String, quantity: Int): Boolean\n}\n\n// domain/port/OrderRepository.kt\ninterface OrderRepository {\n fun save(order: Order): Order\n fun findById(id: String): Order?\n}\n\n\nध्यान दें: कहीं भी डेटाबेस, HTTP या फ्रेमवर्क का उल्लेख नहीं है। शुद्ध व्यावसायिक तर्क।
\n\nचरण 2. व्यावसायिक सेवा
\n\nअब OrderService लिखते हैं, जो इन पोर्ट का उपयोग करता है। Kotlin में, हम कंस्ट्रक्टर के माध्यम से निर्भरताएँ पास करते हैं - इसे निर्भरता इंजेक्शन (DI) कहा जाता है।
// domain/service/OrderService.kt\nclass OrderService(\n private val productRepository: ProductRepository,\n private val orderRepository: OrderRepository\n) {\n fun createOrder(customerId: String, items: List<OrderItem>): Order {\n // प्रत्येक उत्पाद की उपलब्धता की जाँच करें\n for (item in items) {\n if (!productRepository.checkA