DDD सिर्फ एक फैशनेबल शब्द नहीं, बल्कि प्रोजेक्ट बचाने का उपकरण क्यों है
\n\nहर Java डेवलपर कभी न कभी ऐसी स्थिति का सामना करता है: कोड काम करता है, बिज़नेस लॉजिक सर्विसेज में बिखरा हुआ है, और छह महीने बाद प्रोजेक्ट को केवल उसका लेखक ही समझ पाता है (और वह भी मुश्किल से)। परिचित लगता है? यह क्लासिक anemic domain model है — जब एंटिटीज केवल गेटर और सेटर वाले POJO होते हैं, और सारा लॉजिक "मोटी" सर्विसेज में रहता है।
\n\nयहाँ पर Domain-Driven Design (DDD) आता है — एक दृष्टिकोण जो विकास के केंद्र में तकनीकी विवरणों के बजाय बिज़नेस लॉजिक को रखता है। और नहीं, यह कोई और "सैद्धांतिक" पद्धति नहीं है। Java प्रोजेक्ट्स में DDD ठोस पैटर्न प्रदान करता है जो अव्यवस्था को स्वच्छ, रखरखाव योग्य आर्किटेक्चर में बदल देते हैं। इस लेख में हम DDD को व्यवहार में समझेंगे: कोड, उदाहरणों और बिना अनावश्यक बातों के।
\n\nसामरिक बनाम रणनीतिक DDD: Java प्रोजेक्ट के लिए क्या चुनें?
\n\nDDD दो स्तरों में विभाजित है: रणनीतिक और सामरिक। पहला एक बड़ी प्रणाली को Bounded Contexts (संदर्भों) में विभाजित करने और उनके बीच संचार स्थापित करने में मदद करता है। दूसरा कोड के लिए ठोस निर्माण खंड प्रदान करता है: Entity, Value Object, Aggregate, Repository, Domain Event, आदि।
\n\nJava डेवलपर के लिए, सामरिक DDD वह है जहाँ से शुरुआत करनी चाहिए। यह ऐसे उपकरण देता है जिन्हें आज ही लागू किया जा सकता है। लेकिन संदर्भों की रणनीतिक समझ के बिना, आप एक "सार्वभौमिक" मॉडल बनाने का जोखिम उठाते हैं जो किसी के लिए उपयुक्त नहीं है। इसलिए छोटे से शुरू करते हैं: एक सरल लेकिन वास्तविक डोमेन — ऑर्डर प्रबंधन प्रणाली — के उदाहरण पर मुख्य पैटर्न को समझते हैं।
\n\nJava पर DDD के निर्माण खंड: Entity से Aggregate तक
\n\n1. Entity (सत्ता) — हर एक की अपनी पहचान होती है
\n\nDDD में Entity एक ऐसी वस्तु है जिसका एक अद्वितीय पहचानकर्ता और एक सतत जीवन रेखा होती है। आप इसके सभी फ़ील्ड बदल सकते हैं, लेकिन यह वही सत्ता रहेगी। Java में यह केवल POJO नहीं है — यह व्यवहार वाली वस्तु है।
\n\nउदाहरण: ऑनलाइन स्टोर में ऑर्डर। भले ही ऑर्डर की स्थिति बदल जाए, यह वही ऑर्डर है जिसका वही ID है।
\n\npublic class Order {\n private final OrderId id; // Value Object, प्रिमिटिव नहीं\n private OrderStatus status;\n private List<OrderLineItem> items;\n private Money totalAmount;\n\n public Order(OrderId id) {\n this.id = id;\n this.status = OrderStatus.PENDING;\n this.items = new ArrayList<>();\n this.totalAmount = Money.ZERO;\n }\n\n public void addItem(Product product, int quantity) {\n // बिज़नेस नियम: पुष्टि किए गए ऑर्डर में आइटम नहीं जोड़ सकते\n if (this.status != OrderStatus.PENDING) {\n throw new IllegalStateException("Cannot add items to a non-pending order");\n }\n OrderLineItem item = new OrderLineItem(product, quantity);\n items.add(item);\n recalculateTotal();\n }\n\n public void confirm() {\n if (items.isEmpty()) {\n throw new IllegalStateException("Cannot confirm an empty order");\n }\n this.status = OrderStatus.CONFIRMED;\n // यहाँ डोमेन इवेंट ट्रिगर किया जा सकता है\n // registerEvent(new OrderConfirmedEvent(this.id));\n }\n\n private void recalculateTotal() {\n this.totalAmount = items.stream()\n .map(OrderLineItem::getSubtotal)\n .reduce(Money.ZERO, Money::add);\n }\n\n // गेटर — केवल पढ़ने के लिए, सेटर नहीं!\n public OrderId getId() { return id; }\n public OrderStatus getStatus() { return status; }\n public List<OrderLineItem> getItems() { return Collections.unmodifiableList(items); }\n public Money getTotalAmount() { return totalAmount; }\n}\n\nध्यान दें: OrderId Long नहीं है, बल्कि एक Value Object है। क्यों? क्योंकि प्रिमिटिव प्रकार कोई अर्थ नहीं रखता। OrderId में प्रीफ़िक्स, निर्माण तिथि या प्र