जड़ों को अलग करने के लिए रिपोजिटरी को कम करना


83

वर्तमान में मेरे पास डेटाबेस की प्रत्येक तालिका के लिए एक भंडार है और केवल आगे की जड़ों तक कम करके खुद को DDD के साथ संरेखित करना चाहते हैं।

चलो मान लेते हैं कि मेरे पास निम्नलिखित तालिकाओं हैं, Userऔर Phone। प्रत्येक उपयोगकर्ता के पास एक या अधिक फ़ोन हो सकते हैं। सकल जड़ की धारणा के बिना मैं ऐसा कुछ कर सकता हूं:

//assuming I have the userId in session for example and I want to update a phone number
List<Phone> phones = PhoneRepository.GetPhoneNumberByUserId(userId);
phones[0].Number = “911”;
PhoneRepository.Update(phones[0]);

अभ्यास की तुलना में समग्र जड़ों की अवधारणा कागज पर समझना आसान है। मेरे पास कभी भी फ़ोन नंबर नहीं होंगे जो किसी उपयोगकर्ता से संबंधित नहीं हैं, इसलिए क्या यह PhoneRepository के साथ दूर करना और फ़ोन से संबंधित विधियों को UserRepository में शामिल करना होगा? उत्तर को हां मानते हुए, मैं पूर्व कोड नमूने को फिर से लिखने जा रहा हूं।

क्या मुझे UserRepository पर एक विधि की अनुमति है जो फ़ोन नंबर लौटाती है? या इसे हमेशा किसी उपयोगकर्ता के संदर्भ में लौटा देना चाहिए, और फिर फ़ोन नंबर प्राप्त करने के लिए उपयोगकर्ता के माध्यम से संबंध को पीछे छोड़ना चाहिए:

List<Phone> phones = UserRepository.GetPhoneNumbers(userId);
// Or
User user = UserRepository.GetUserWithPhoneNumbers(userId); //this method will join to Phone

इसके बावजूद कि मैं किस तरह से फोन हासिल करता हूं, मुझे लगता है कि मैंने उनमें से एक को संशोधित किया है, मैं उन्हें कैसे अपडेट करूं? मेरी सीमित समझ यह है कि रूट के तहत वस्तुओं को रूट के माध्यम से अपडेट किया जाना चाहिए, जो मुझे नीचे # 1 विकल्प की ओर ले जाएगा। हालाँकि यह पूरी तरह से Entity फ्रेमवर्क के साथ अच्छी तरह से काम करेगा, यह बेहद अन-डिस्क्रिप्टिव लगता है, क्योंकि कोड को पढ़ने से मुझे कोई अंदाजा नहीं है कि मैं वास्तव में क्या अपडेट कर रहा हूं, भले ही Entity फ्रेमवर्क ग्राफ के भीतर बदली हुई वस्तुओं पर नजर रख रहा हो।

UserRepository.Update(user);
// Or
UserRepository.UpdatePhone(phone);

अन्त में, मैं इस तरह के रूप में कई देखने टेबल वास्तव में कुछ से जुड़ा नहीं है कि, है यह सोचते हैं CountryCodes, ColorsCodes, SomethingElseCodes। मैं उन्हें ड्रॉप डाउन या अन्य किसी भी कारण से आबाद करने के लिए उपयोग कर सकता हूं। क्या ये स्टैंडअलोन रिपॉजिटरी हैं? क्या उन्हें किसी प्रकार के तार्किक समूह / भंडार में जोड़ा जा सकता है CodesRepository? या यह कि सर्वोत्तम प्रथाओं के खिलाफ है।


2
वास्तव में एक बहुत अच्छा सवाल, कि मैं अपने आप से बहुत संघर्ष कर रहा हूं। उन ट्रेड-ऑफ पॉइंट्स में से एक की तरह लगता है, जहां "सही" समाधान नहीं है। जब मैं इसे लिखता हूं, तब उपलब्ध उत्तर अच्छे होते हैं और अधिकांश समस्याओं को शामिल करते हैं, मुझे ऐसा नहीं लगता कि वे कोई "अंतिम" समाधान प्रदान करते हैं: :(
cwap

मैं आपको सुनता हूं, "सही" समाधान के करीब कोई सीमा नहीं है जो कोई भी प्राप्त कर सकता है। मुझे लगता है कि जब तक हम बेहतर तरीके से नहीं सीखते हैं, तब तक हमें अपने सर्वश्रेष्ठ के साथ बनाना है :)
e36M3

+1 - मैं इससे भी जूझ रहा हूं। इससे पहले कि मैं हर टेबल के लिए अलग रेपो और सर्विस लेयर था। मैंने इन्हें संयोजित करना शुरू कर दिया, जहाँ यह समझ में आता है, लेकिन फिर मैंने कोड की 1k लाइनों के साथ रेपो और सर्विस लेयर को समाप्त कर दिया। अपने नवीनतम एप्लिकेशन स्लाइस में, मैंने केवल उसी रेपो / सर्विस लेयर में बारीकी से संबंधित अवधारणाओं को रखने के लिए थोड़ा समर्थन किया है, भले ही वह आइटम निर्भर हो। जैसे - एक ब्लॉग के लिए, मैं पोस्ट रेपो एग्रीगेट में टिप्पणियां जोड़ रहा था, लेकिन अब मैंने उन्हें अलग टिप्पणी रेपो / सेवा से अलग कर दिया है।
जुपशुक

जवाबों:


12

आपको अपनी रिपॉजिटरी में अपनी इच्छानुसार कोई भी विधि रखने की अनुमति है :) आपके द्वारा उल्लिखित दोनों ही मामलों में, यह उपयोगकर्ता को फोन सूची वाली आबादी को वापस करने के लिए समझ में आता है। आम तौर पर उपयोगकर्ता वस्तु पूरी तरह से सभी उप सूचनाओं (सभी पते, फोन नंबर कहते हैं) के साथ आबाद नहीं होगी और हमारे पास उपयोगकर्ता वस्तु को विभिन्न प्रकार की जानकारी प्राप्त करने के लिए अलग-अलग तरीके हो सकते हैं। इसे आलसी लोडिंग कहा जाता है।

User GetUserDetailsWithPhones()
{
    // Populate User along with Phones
}

अपडेट करने के लिए, इस मामले में, उपयोगकर्ता को अपडेट किया जा रहा है, न कि फोन नंबर ही। स्टोरेज मॉडल फोन को अलग-अलग टेबल में स्टोर कर सकता है और इस तरह से आप सोच सकते हैं कि सिर्फ फोन को अपडेट किया जा रहा है लेकिन डीडीडी के नजरिए से ऐसा नहीं है। जहाँ तक पठनीयता का सवाल है, जबकि रेखा का

UserRepository.Update(user)

अकेले यह बताता नहीं है कि क्या अपडेट किया जा रहा है, ऊपर दिया गया कोड यह स्पष्ट कर देगा कि क्या अपडेट किया जा रहा है। इसके अलावा यह सबसे अधिक संभावना है कि फ्रंट एंड मेथड कॉल का हिस्सा हो सकता है जो कि अपडेट किया जा रहा है पर हस्ताक्षर कर सकता है।

लुकअप टेबल के लिए, और वास्तव में भी अन्यथा, यह GenericRepository है और इसका उपयोग करने के लिए उपयोगी है। कस्टम रिपॉजिटरी GenericRepository से विरासत में मिल सकती है।

public class UserRepository : GenericRepository<User>
{
    IEnumerable<User> GetUserByCustomCriteria()
    {
    }

    User GetUserDetailsWithPhones()
    {
        // Populate User along with Phones
    }

    User GetUserDetailsWithAllSubInfo()
    {
        // Populate User along with all sub information e.g. phones, addresses etc.
    }
}

जेनेरिक रिपॉजिटरी एंटिटी फ्रेमवर्क के लिए खोजें और आप कई अच्छे कार्यान्वयन को ठीक करेंगे। उनमें से एक का उपयोग करें या अपना खुद का लिखें।


@amit_g, जानकारी के लिए धन्यवाद। मैं पहले से ही एक जेनेरिक / बेस रिपॉजिटरी का उपयोग करता हूं जिसमें से अन्य सभी को विरासत में मिला है। एक रिपॉजिटरी में "लुकअप" तालिकाओं के एक तार्किक समूह के लिए मेरा विचार बस समय बचाने के लिए और रिपॉजिटरी की संख्या में कटौती करना था। इसलिए ColorCodeRepository और OtherCodeRepository बनाने के बजाय, मैं केवल CodesRepository.GetColorCodes () और CodesRepository.GetAnotherCodes () बनाऊंगा। लेकिन मुझे यकीन नहीं है कि एक रिपॉजिटरी में असंबंधित संस्थाओं का एक तार्किक समूहन बुरा व्यवहार है।
e36M3

इसके अलावा, आप फिर इस बात की पुष्टि कर रहे हैं कि DDD नियमों के अनुसार, जड़ के अनुरूप भंडार में विधियाँ मूल को लौटाना चाहिए और ग्राफ के भीतर अंतर्निहित संस्थाओं को नहीं। इसलिए मेरे उदाहरण में UserRepository पर कोई भी विधि केवल उपयोगकर्ता प्रकार वापस कर सकती है, भले ही शेष ग्राफ़ कैसा दिखता हो (या ग्राफ़ का वह भाग जो मुझे वास्तव में इस तरह के पते या फ़ोनों में दिलचस्पी है)?
e36M3

CodesRepository ठीक है, लेकिन इसमें जो है उसे लगातार बनाए रखना मुश्किल होगा। इसे केवल GenericRepository <ColorCodes> GetAll () द्वारा पूरा किया जा सकता है। चूंकि GenericRepository में केवल बहुत सामान्य तरीके (GetAll, GetByID आदि) होंगे, यह लुकअप तालिकाओं के लिए ठीक काम करेगा।
amit_g

1
@ e36M3, हां। उदाहरण के लिए geekswithblogs.net/seanfao/archive/2009/12/03/136680.aspx
amit_g

2
दुर्भाग्य से यह उत्तर गलत है। रिपॉजिटरी को इन-मेमोरी ऑब्जेक्ट्स के संग्रह के रूप में माना जाना चाहिए और आपको आलसी लोडिंग से बचना चाहिए। यहाँ उस besnikgeek.blogspot.com/2010/07/… के
Rafał Łu Octy'ski

9

एग्रीगेट रूट रिपॉजिटरी पर आपका उदाहरण पूरी तरह से ठीक है अर्थात कोई भी इकाई जो किसी अन्य पर निर्भरता के बिना यथोचित रूप से मौजूद नहीं हो सकती है, उसका अपना रिपॉजिटरी (आपके मामले में फोन) नहीं होना चाहिए। इस विचार के बिना आप जल्दी से अपने आप को 1-1 मानचित्रण से डीबी तालिकाओं में रिपोजिटरी के विस्फोट के साथ पा सकते हैं।

आपको स्वयं के रिपॉजिटरी के बजाय डेटा परिवर्तनों के लिए यूनिट ऑफ़ वर्क पैटर्न का उपयोग करना चाहिए, क्योंकि मुझे लगता है कि वे आपको इरादे के बारे में कुछ भ्रम पैदा कर रहे हैं जब यह डीबी में परिवर्तन को जारी रखने की बात आती है। एक EF समाधान में कार्य की इकाई अनिवार्य रूप से आपके EF संदर्भ के आसपास एक इंटरफ़ेस आवरण है।

लुकअप डेटा के लिए आपकी रिपॉजिटरी के संबंध में, हम केवल एक ReferenceDataRepository बनाते हैं जो डेटा के लिए ज़िम्मेदार हो जाता है जो विशेष रूप से एक डोमेन इकाई (देश, रंग आदि) से संबंधित नहीं है।


1
धन्यवाद। मुझे यकीन नहीं है कि यूनिट ऑफ़ वर्क रिपॉजिटरी को कैसे बदल देता है? मैं पहले से ही UOW को इस अर्थ में नियोजित करता हूं कि प्रत्येक व्यवसाय लेनदेन के अंत में (HTTP अनुरोध के अंत) में इकाई फ्रेमवर्क संदर्भ में एक ही SaveChanges () कॉल होगा। हालाँकि मैं अभी भी डेटा एक्सेस के लिए रिपॉजिटरी (उस घर में EF संदर्भ) से गुजरता हूं। जैसे UserRepository.Delete (उपयोगकर्ता) और UserRepository.Add (उपयोगकर्ता)।
e36M3

5

यदि फोन कोई अर्थ नहीं बनाता है w / o उपयोगकर्ता, तो यह एक इकाई है (यदि आप इसकी पहचान के बारे में परवाह करते हैं) या मूल्य वस्तु है और इसे हमेशा उपयोगकर्ता के माध्यम से संशोधित किया जाना चाहिए और एक साथ पुनर्प्राप्त / अपडेट किया जाना चाहिए।

संदर्भ परिभाषा के रूप में समग्र जड़ों के बारे में सोचें - वे स्थानीय संदर्भों को आकर्षित करते हैं लेकिन वैश्विक संदर्भ (आपका आवेदन) में स्वयं हैं।

यदि आप डोमेन संचालित डिज़ाइन का अनुसरण करते हैं, तो रिपॉजिटरी को 1: 1 प्रति कुल मूल माना जाता है।
कोई बहना नहीं।

मुझे यकीन है कि ये समस्याएं हैं जिनका आप सामना कर रहे हैं:

  • तकनीकी कठिनाइयों - वस्तु संबंध प्रतिबाधा बेमेल। आप पूरे ऑब्जेक्ट ग्राफ को आसानी और इकाई ढांचे के साथ बनाए रखने में मदद कर रहे हैं।
  • डोमेन मॉडल डेटा केंद्रित है (व्यवहार केंद्रित के विपरीत)। उसके कारण - आप वस्तु पदानुक्रम (पहले उल्लिखित संदर्भ) के बारे में ज्ञान खो देते हैं और जादुई रूप से सब कुछ एक मूल जड़ बन जाता है।

मुझे यकीन नहीं है कि पहली समस्या को कैसे ठीक किया जाए, लेकिन मैंने देखा है कि दूसरे को ठीक करना पहले से काफी अच्छा है। व्यवहार केंद्रित के साथ मेरा क्या मतलब है यह समझने के लिए, इस पेपर को आज़माएं।

Ps सकल भंडार को कम करने का कोई मतलब नहीं है।
Pps से बचें "CodeRepositories"। यह डेटा केंद्रित -> प्रक्रियात्मक कोड की ओर जाता है।
Ppps कार्य पैटर्न की इकाई से बचें। अलग-अलग जड़ों को लेन-देन की सीमाओं को परिभाषित करना चाहिए।


1
चूंकि पेपर का लिंक अब सक्रिय नहीं है, इसके बजाय इसका उपयोग करें: web.archive.org/web/20141021055503/http://www.objectmentor.com/…
JwJosefy

3

यह एक पुराना प्रश्न है, लेकिन एक सरल समाधान पोस्ट करने के लायक है।

  1. EF Context आपको पहले से ही Unit (कार्य परिवर्तन) और Repositories (DB से सामान के लिए स्मृति संदर्भ) दोनों प्रदान कर रहा है। इसके अलावा अमूर्तता अनिवार्य नहीं है।
  2. अपने संदर्भ वर्ग से DBSet निकालें, क्योंकि फ़ोन एक समग्र रूट नहीं है।
  3. इसके बजाय उपयोगकर्ता पर 'फ़ोन' नेविगेशन गुण का उपयोग करें।

स्थिर शून्य अपडेटनंबर (int userId, स्ट्रिंग ओल्डनंबर, स्ट्रिंग न्यूनंबर)

static void updateNumber(int userId, string oldNumber, string newNumber)
    {
        using (MyContext uow = new MyContext()) // Unit of Work
        {
            DbSet<User> repo = uow.Users; // Repository
            User user = repo.Find(userId); 
            Phone oldPhone = user.Phones.Where(x => x.Number.Trim() == oldNumber).SingleOrDefault();
            oldPhone.Number = newNumber;
            uow.SaveChanges();
        }

    }

अमूर्तता अनिवार्य नहीं है, लेकिन अनुशंसित है। इकाई ढांचा अभी भी केवल एक प्रदाता और बुनियादी ढांचे का हिस्सा है। यह केवल इस बात का भी नहीं है कि प्रदाता बदल गए तो क्या होना चाहिए, लेकिन बड़ी प्रणालियों में आपके पास कई प्रकार के प्रदाता हो सकते हैं जो विभिन्न डोमेन अवधारणाओं को अलग-अलग दृढ़ता माध्यमों के लिए बनाए रखते हैं। यह अमूर्तता का एक प्रकार है जो पर्याप्त रूप से जल्दी बनाने के लिए आसान है, लेकिन पर्याप्त समय और जटिलता की ओर रिफ्लेक्टर के लिए दर्दनाक है।
जोसेफ फेरिस

1
जब मैं एक रिपॉजिटरी इंटरफ़ेस की कोशिश करता हूं और अमूर्त होता है, तो ईएफ के ओआरएम (जैसे आलसी लोडिंग, क्वेरीज़) के लाभों को बनाए रखना बहुत मुश्किल होता है।
चाल्की

यह सुनिश्चित करने के लिए एक दिलचस्प चर्चा है। चूंकि आलसी लोडिंग कार्यान्वयन के लिए बहुत विशिष्ट है, मुझे लगता है कि इसका मूल्य आधारभूत संरचना (लेयर बाउंडेड ट्रांसलेशन के साथ डोमेन ऑब्जेक्ट) तक सीमित है। जब मैंने जेनेरिक एब्स्ट्रैक्शन का प्रयास किया है, तो बहुत सारे कार्यान्वयन जो मैंने मुद्दों में देखे हैं। मैं एक स्पष्ट कार्यान्वयन के साथ जाना चाहता हूं, क्योंकि सामान्य विधियों में बहुत कम डोमेन मूल्य है। EF क्वेरी को अत्यधिक प्रयोग करने योग्य बनाता है, लेकिन समस्या रिपॉजिटरी की भूमिका बन जाती है - अर्थात नियंत्रकों द्वारा उपयोग की जाने वाली रिपॉजिटरी में एब्सट्रैक्शन के लाभ याद आते हैं।
जोसेफ फेरिस

0

यदि एक फोन इकाई केवल एक मूल रूट उपयोगकर्ता के साथ मिलकर समझ में आता है, तो मुझे यह भी लगता है कि यह समझ में आता है कि एक नया फोन रिकॉर्ड जोड़ने के लिए उपयोगकर्ता एक विशिष्ट विधि (डीडीडी व्यवहार) के माध्यम से उपयोगकर्ता डोमेन ऑब्जेक्ट की जिम्मेदारी है और वह कर सकता है कई कारणों से पूरी तरह से समझ में आता है, इसका बड़ा कारण यह है कि हमें जांच करनी चाहिए कि उपयोगकर्ता ऑब्जेक्ट मौजूद है क्योंकि फ़ोन निकाय इस अस्तित्व पर निर्भर करता है और संभवत: किसी अन्य प्रक्रिया को रूट एग्रीगेट को हटाने से पहले कोई और प्रक्रिया सुनिश्चित करने के लिए अधिक सत्यापन जाँच करते समय इस पर लेनदेन लॉक रखें हम ऑपरेशन को सत्यापित कर रहे हैं। अन्य प्रकार के रूट एग्रीगेट्स के साथ अन्य मामलों में आप कुछ मूल्य को एकत्र करना या गणना करना चाहते हैं और बाद में अन्य ऑपरेशन्स द्वारा अधिक कुशल प्रसंस्करण के लिए रूट एग्रीगेट के कॉलम गुणों पर इसे बनाए रख सकते हैं।

इसके अलावा, यदि आप उन तरीकों का उपयोग करना चाहते हैं, जो सभी फ़ोनों को प्राप्त करते हैं, भले ही उपयोगकर्ता उन्हें अपने पास रखने के बावजूद, भले ही उपयोगकर्ता रिपॉजिटरी के माध्यम से आपको केवल एक विधि की आवश्यकता हो, सभी उपयोगकर्ताओं को IQueryable के रूप में लौटाता है, तो आप उन्हें सभी उपयोगकर्ता फ़ोन प्राप्त करने और परिष्कृत करने के लिए मैप कर सकते हैं उसके साथ क्वेरी करें। तो आप भी इस मामले में एक PhoneRepository की जरूरत नहीं है। इसके बजाय मैं IQueryable के लिए एक्सटेंशन पद्धति के साथ एक वर्ग का उपयोग करूंगा जो कि मैं किसी रिपॉजिटरी वर्ग से न केवल कहीं भी उपयोग कर सकता हूं अगर मैं विधियों के पीछे प्रश्नों को सार करना चाहता था।

केवल डोमेन ऑब्जेक्ट का उपयोग करके फ़ोन एंटिटीज़ को हटाने में सक्षम होने के लिए केवल एक कैविटी और फ़ोन रिपॉज़िटरी के लिए आपको यह सुनिश्चित करने की आवश्यकता नहीं है कि UserId फ़ोन प्राथमिक कुंजी का हिस्सा है या दूसरे शब्दों में फ़ोन रिकॉर्ड की प्राथमिक कुंजी एक संयुक्त कुंजी है फोन इकाई में उपयोगकर्ता आईडी और कुछ अन्य संपत्ति (मैं एक ऑटो उत्पन्न पहचान का सुझाव देता हूं) से बना हूं। यह स्पष्ट रूप से समझ में आता है क्योंकि उपयोगकर्ता रिकॉर्ड द्वारा फोन रिकॉर्ड "स्वामित्व" है और यह उपयोगकर्ता नेविगेशन संग्रह से हटा रहा है डेटाबेस से इसका पूर्ण निष्कासन बराबर होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.