C# में Domain-Driven Design (DDD): जटिलता में न डूबें
\n\nजब आपका C# प्रोजेक्ट "साधारण CRUD एप्लिकेशन" वाले चरण से आगे बढ़ता है, तो असली परीक्षा शुरू होती है। बिजनेस लॉजिक उलझने लगता है, सर्विस मेथड्स सैकड़ों लाइनों में फैल जाते हैं, और "रीफैक्टरिंग" शब्द सुनते ही कंपकंपी छूट जाती है। परिचित लग रहा है? आपका स्वागत है उस दुनिया में जहां Domain-Driven Design (DDD) के बिना आपका कोड दलदल में बदल सकता है।
\n\nDDD कोई "एक और फ्रेमवर्क" नहीं है। यह एक दर्शन है जो बिजनेस डोमेन को ब्रह्मांड का केंद्र बनाता है। आज हम देखेंगे कि C# प्रोजेक्ट्स में Domain-Driven Design को कैसे लागू करें ताकि कोड न सिर्फ काम करे, बल्कि समझने और बदलने में भी आसान हो। कोई बेकार की बात नहीं - सिर्फ असली उदाहरण और व्यावहारिक सुझाव।
\n\nDDD C# प्रोजेक्ट्स को क्यों बचाता है?
\n\nमान लीजिए आप एक ऑनलाइन स्टोर के लिए ऑर्डर मैनेजमेंट सिस्टम बना रहे हैं। अगर आप सारी लॉजिक ASP.NET कंट्रोलर्स में डाल देंगे, तो छह महीने बाद आप खुद नहीं समझा पाएंगे कि CreateOrder() मेथड CalculateShipping() को क्यों कॉल करता है, जो आगे जाकर करेंसी रेट्स के लिए डेटाबेस में झांकता है। DDD एक साफ विभाजन प्रस्तावित करता है: एक डोमेन लेयर होती है जहां बिजनेस लॉजिक रहता है, और एक इन्फ्रास्ट्रक्चर होती है जो तकनीकी विवरण संभालती है।
DDD की मुख्य अवधारणा है Ubiquitous Language (सर्वव्यापी भाषा)। आप और बिजनेस एक्सपर्ट एक ही भाषा में बात करते हैं। कोड में यह "Order", "LineItem", "DiscountPolicy" जैसे शब्दों में दिखता है, न कि "OrderDto", "OrderService" या "Helper" में। कोड जितना बिजनेस की भाषा के करीब होगा, उसे मेंटेन करना उतना ही आसान होगा।
\n\nस्ट्रैटेजिक और टैक्टिकल डिज़ाइन
\n\nDDD दो भागों में बंटा है: स्ट्रैटेजिक (सिस्टम को मॉड्यूल में कैसे बांटें) और टैक्टिकल (मॉड्यूल के अंदर कोड कैसे लिखें)। C# में टैक्टिकल डिज़ाइन हमें चार शक्तिशाली बिल्डिंग ब्लॉक देता है:
\n\n- \n
- Entity — एक अनोखी पहचान (जैसे
OrderId) वाला ऑब्जेक्ट। \n - Value Object — एक ऑब्जेक्ट जो अपनी विशेषताओं से परिभाषित होता है (जैसे
MoneyयाAddress)। \n - Aggregate Root — मूल एंटिटी जो ऑब्जेक्ट्स के समूह को नियंत्रित करती है। \n
- Repository — डेटा एक्सेस का एब्स्ट्रैक्शन। \n
आइए देखें कि कोड में यह कैसा दिखता है।
\n\nउदाहरण: ऑर्डर (Order) डिज़ाइन करना
\n\nचलिए सरल शुरू करते हैं: हमारे पास एक ऑर्डर है जिसमें कई आइटम हैं। ऑर्डर की स्थिति "नया", "भुगतान किया गया" या "रद्द" हो सकती है।
\n\n1. Value Object: Money
\n\nपैसा एक क्लासिक Value Object है। दो Money ऑब्जेक्ट बराबर हैं अगर उनकी राशि और मुद्रा समान है। वे अपरिवर्तनीय (immutable) होते हैं।
public sealed record Money(decimal Amount, string Currency)\n{\n public static Money operator +(Money left, Money right)\n {\n if (left.Currency != right.Currency)\n throw new InvalidOperationException("मुद्रा मेल नहीं खाती");\n return new Money(left.Amount + right.Amount, left.Currency);\n }\n}\n\n\nध्यान दें record पर — C# 9+ में यह Value Object बनाने का आदर्श तरीका है। यह अपने आप Equals, GetHashCode और इम्यूटेबिलिटी प्रदान करता है।
2. Entity: Order और LineItem
\n\nएंटिटीज की एक पहचान होती है। Entity के लिए एक बेस क्लास बनाते हैं:
\n\npublic abstract class Entity<TId>\n{\n public TId Id { get; protected set; }\n\n protected Entity(TId id) => Id = id;\n\n public override bool Equals(object? obj) =>\n obj is Entity<TId> other && Id!.Equals(other.Id);\n\n public override int GetHashCode() => Id!.GetHashCode();\n}\n\n\nअब खुद Order और LineItem:
public class Order : Entity<Guid>\n{\n private readonly List<LineItem> _items = new();\n public IReadOnlyList<LineItem> Items => _items.AsReadOnly();\n public OrderStatus Status { get; private set; }\n public Money Total { get; private set; }\n