हेक्सागोनल आर्किटेक्चर सिर्फ एक फैशनेबल शब्द क्यों नहीं है, बल्कि आपके कोड के लिए वरदान है?
\nकल्पना करें कि आप एक घर बना रहे हैं। क्या आप सॉकेट को कंक्रीट की दीवारों में चिपका देते हैं? आप पहले वायरिंग बिछाते हैं, मार्किंग करते हैं, और फिर ध्यान से सॉकेट को जोड़ते हैं। सॉफ्टवेयर डेवलपमेंट में भी बिल्कुल ऐसा ही है — आपका बिजनेस लॉजिक (एप्लिकेशन का कोर) इस बात पर निर्भर नहीं होना चाहिए कि उससे किस माध्यम से संवाद किया जा रहा है: REST API, डेटाबेस या मैसेज क्यू।
\nहेक्सागोनल आर्किटेक्चर (जिसे "पोर्ट और एडेप्टर" या एलिस्टेयर कोबर्न की व्याख्या में "स्वच्छ आर्किटेक्चर" भी कहा जाता है) इसी समस्या का समाधान करता है। इस लेख में, हम वास्तविक कोड उदाहरणों के साथ TypeScript में इस आर्किटेक्चर को लागू करने का तरीका समझेंगे। आप सीखेंगे कि बिजनेस लॉजिक को इन्फ्रास्ट्रक्चर से कैसे अलग करें, कोड को परीक्षण योग्य और लचीला कैसे बनाएं।
\n\nहेक्सागोनल आर्किटेक्चर सरल शब्दों में क्या है?
\nएक षट्भुज (हेक्सागोन) की कल्पना करें। केंद्र में आपका एप्लिकेशन बिजनेस लॉजिक के साथ है। प्रत्येक किनारे पर एक "पोर्ट" (इंटरफ़ेस) है, जिसके माध्यम से बाहरी दुनिया कोर के साथ संवाद कर सकती है। एडेप्टर विशिष्ट कार्यान्वयन हैं: कंट्रोलर, रिपॉजिटरी, बाहरी सेवाओं के क्लाइंट। वे पोर्ट से जुड़ते हैं, जैसे प्लग सॉकेट में।
\nमुख्य सिद्धांत: निर्भरताएं अंदर की ओर निर्देशित होती हैं। बाहरी कोड (डेटाबेस, फ्रेमवर्क) कोर पर निर्भर करता है, न कि इसके विपरीत। यह आपको बिजनेस लॉजिक की एक भी पंक्ति को छुए बिना PostgreSQL से MongoDB में डेटाबेस बदलने की अनुमति देता है।
\n\nमुख्य अवधारणाएं
\n- \n
- कोर (Domain) — बिजनेस लॉजिक, एंटिटी, वैल्यू ऑब्जेक्ट, यूज़ केस। इसकी कोई बाहरी निर्भरता नहीं है। \n
- पोर्ट (Port) — एक इंटरफ़ेस (TypeScript में यह एक abstract class या interface है) जो बाहरी दुनिया के लिए अनुबंध को परिभाषित करता है। \n
- एडेप्टर (Adapter) — पोर्ट का एक विशिष्ट कार्यान्वयन। उदाहरण के लिए, एक रिपॉजिटरी जो TypeORM के माध्यम से PostgreSQL के साथ काम करती है। \n
- एप्लिकेशन (Application) — वह परत जो यूज़ केस के निष्पादन का समन्वय करती है। \n
व्यावहारिक उदाहरण: ऑर्डर प्रबंधन प्रणाली
\nआइए एक सरल ऑर्डर सिस्टम डिज़ाइन करें। हमारे पास Order और Product एंटिटी, CreateOrder यूज़ केस, रिपॉजिटरी के लिए पोर्ट और PostgreSQL पर एक एडेप्टर होगा।
चरण 1. कोर (Domain layer) को परिभाषित करना
\nएक domain/ फ़ोल्डर बनाएं। यहां एंटिटी और पोर्ट के लिए इंटरफ़ेस रहते हैं। फ्रेमवर्क पर कोई निर्भरता नहीं!
// domain/order.entity.ts\nexport class Order {\n constructor(\n public readonly id: string,\n public items: OrderItem[],\n public status: 'pending' | 'confirmed' | 'shipped',\n public total: number\n ) {}\n\n public addItem(productId: string, quantity: number, price: number): void {\n this.items.push({ productId, quantity, price });\n this.total = this.calculateTotal();\n }\n\n private calculateTotal(): number {\n return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n }\n}\n\nexport interface OrderItem {\n productId: string;\n quantity: number;\n price: number;\n}\n\n\nअब रिपॉजिटरी के लिए पोर्ट को परिभाषित करें:
\n\n// domain/ports/order.repository.interface.ts\nimport { Order } from '../order.entity';\n\nexport interface IOrderRepository {\n save(order: Order): Promise<void>;\n findById(id: string): Promise<Order | null>;\n findAll(): Promise<Order[]>;\n}\n\n\nचरण 2. यूज़ केस — एप्लिकेशन का हृदय
\nयूज़ केस एक उपयोग परिदृश्य है। इसमें बिजनेस लॉजिक होता है और यह पोर्ट का उपयोग करता है, लेकिन यह नहीं जानता कि उन्हें कौन लागू करता है।
\n\n// application/use-cases/create-order.use-case.ts\nimport { Order } from '../../domain/order.entity';\nimport { IOrderRepository } from '../../domain/ports/order.repository.interface';\n\nexport class CreateOrderUseCase {\n constructor(private readonly orderRepository: IOrderRepository) {}\n\n async execute(use