DDD में, रिपॉजिटरी को एक इकाई या डोमेन ऑब्जेक्ट को उजागर करना चाहिए?


11

जैसा कि मैंने इसे समझा, DDD में, कुल रूट के साथ रिपॉजिटरी पैटर्न का उपयोग करना उचित है। मेरा सवाल है, क्या मुझे डेटा को एक इकाई या डोमेन ऑब्जेक्ट / डीटीओ के रूप में वापस करना चाहिए?

हो सकता है कि कुछ कोड मेरे प्रश्न को आगे बताएंगे:

सत्ता

public class Customer
{
  public Guid Id { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
}

क्या मुझे ऐसा कुछ करना चाहिए?

public Customer GetCustomerByName(string name) { /*some code*/ }

या इस तरह का कुछ?

public class CustomerDTO
{
  public Guid Id { get; set; }
  public FullName { get; set; }
}

public CustomerDTO GetCustomerByName(string name) { /*some code*/ }

अतिरिक्त प्रश्न:

  1. एक भंडार में, क्या मुझे IQueryable या IEnumerable वापस करना चाहिए?
  2. एक सेवा या भंडार में, मैं की तरह कुछ करना चाहिए .. GetCustomerByLastName, GetCustomerByFirstName, GetCustomerByEmail? या बस एक विधि है कि कुछ की तरह है GetCustomerBy(Func<string, bool> predicate)?

GetCustomerByName('John Smith')यदि आपके डेटाबेस में बीस जॉन स्मिथ हैं तो क्या होगा ? ऐसा लगता है कि आप मान रहे हैं कि दो लोगों का नाम एक जैसा नहीं है।
bdsl

जवाबों:


8

क्या मुझे डेटा को एक इकाई या डोमेन ऑब्जेक्ट / डीटीओ के रूप में वापस करना चाहिए

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

यदि यह मामला है, तो शायद आपको अपने डोमेन मॉडल पर पुनर्विचार करना चाहिए और अपनी बड़ी इकाई को संबंधित छोटी संस्थाओं में विभाजित करना चाहिए।

  1. एक भंडार में, क्या मुझे IQueryable या IEnumerable वापस करना चाहिए?

अंगूठे का एक अच्छा नियम हमेशा सबसे सरल (विरासत में पदानुक्रम में उच्चतम) प्रकार को वापस करना है। तो, लौट IEnumerableजब तक आप एक साथ भंडार उपभोक्ता काम देना चाहते हैं IQueryable

व्यक्तिगत रूप से, मुझे लगता है कि वापसी करना IQueryableएक अस्थिरता है, लेकिन मैंने कई डेवलपर्स से मुलाकात की है जो यह तर्क देते हैं कि ऐसा नहीं है। मेरे दिमाग में सभी क्वेरी तर्क समाहित होने चाहिए और रिपोजिटरी द्वारा छुपाए जाने चाहिए। यदि आप कॉलिंग कोड को उनकी क्वेरी को अनुकूलित करने की अनुमति देते हैं तो रिपॉजिटरी का क्या मतलब है?

  1. किसी सेवा या भंडार में, मुझे कुछ करना चाहिए .. GetCustomerByLastName, GetCustomerByFirstName, GetCustomerByEmail? या बस एक विधि बनाएं जो GetCustomerBy (Func predicate) की तरह कुछ है?

उसी कारण से जैसा मैंने बिंदु 1 में उल्लेख किया है, निश्चित रूप से उपयोग नहीं करते हैं GetCustomerBy(Func<string, bool> predicate)। यह पहली बार में लुभावना लग सकता है, लेकिन यही कारण है कि लोगों ने सामान्य रिपॉजिटरी से नफरत करना सीख लिया है। यह लचर है।

GetByPredicate(Func<T, bool> predicate)जब वे ठोस कक्षाओं के पीछे छिपे होते हैं, तो चीजें केवल उपयोगी होती हैं। इसलिए, यदि आपके पास एक सार आधार वर्ग था, RepositoryBase<T>जिसे उजागर protected T GetByPredicate(Func<T, bool> predicate)किया गया था जो केवल कंक्रीट रिपॉजिटरी (जैसे public class CustomerRepository : RepositoryBase<Customer>) द्वारा उपयोग किया गया था, तो यह ठीक होगा।


तो आपका यह कहना, डोमेन लेयर में DTO क्लासेस होना ठीक है?
15 फरवरी को कोडफिश

यही मैं कहना नहीं चाह रहा था। मैं एक DDD गुरु नहीं हूँ इसलिए मैं यह नहीं कह सकता कि यदि यह स्वीकार्य है। जिज्ञासा से बाहर, आप पूर्ण इकाई को वापस करने के लिए क्यों नहीं थे? क्या यह बहुत बड़ा है?
मेटाफाइट

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

6

उन लोगों का एक बड़ा समुदाय है जो अपने डोमेन को लागू करने के लिए CQRS का उपयोग करते हैं। मेरी भावना यह है कि, यदि आपके भंडार का इंटरफ़ेस उनके द्वारा उपयोग की जाने वाली सर्वोत्तम प्रथाओं के अनुरूप है, तो आप बहुत दूर नहीं भटकेंगे।

मैंने जो देखा है उसके आधार पर ...

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

इसलिए, ऐसे संदर्भों में जहां आप समुच्चय को संशोधित करने जा रहे हैं, मैं रिपॉजिटरी से इकाई (उर्फ मूल जड़) को वापस करने की उम्मीद करूंगा।

2) क्वेरी हैंडलर समुच्चय को बिल्कुल नहीं छूते हैं; इसके बजाय, वे एग्रीगेट्स - वैल्यू ऑब्जेक्ट्स के अनुमानों के साथ काम करते हैं जो किसी समय में एग्रीगेट / एग्रीगेट्स की स्थिति का वर्णन करते हैं। तो एग्रिगेटटीडो के बजाय प्रोग्रेसडॉटो सोचें, और आपके पास सही विचार है।

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

आपके सभी getCustomerByPropertyकॉल मेरे लिए प्रश्नों की तरह दिखते हैं, इसलिए वे बाद की श्रेणी में आते हैं। मैं शायद कलेक्शन जनरेट करने के लिए सिंगल एंट्री पॉइंट का इस्तेमाल करना चाहता हूं, इसलिए मैं यह देखना चाहूंगा कि क्या

getCustomersThatSatisfy(Specification spec)

एक उचित विकल्प है; क्वेरी हैंडलर तब दिए गए मापदंडों से उपयुक्त विनिर्देशन का निर्माण करेंगे, और उस विनिर्देश को रिपॉजिटरी को पास करेंगे। नकारात्मक पक्ष यह है कि हस्ताक्षर वास्तव में सुझाव देते हैं कि भंडार स्मृति में एक संग्रह है; यह मेरे लिए स्पष्ट नहीं है कि विधेय आपको बहुत खरीदता है यदि रिपॉजिटरी एक रिलेशनल डेटाबेस के खिलाफ SQL स्टेटमेंट चलाने के लिए केवल एक अमूर्त है।

कुछ पैटर्न हैं जो हालांकि मदद कर सकते हैं। उदाहरण के लिए, हाथ से विनिर्देश के निर्माण के बजाय, रिपॉजिटरी को बाधाओं का वर्णन करने के लिए पास करें, और रिपॉजिटरी के कार्यान्वयन को यह तय करने की अनुमति दें कि क्या करना है।

चेतावनी: जावा टाइपिंग का पता चला

interface CustomerRepository {
    interface ConstraintBuilder {
        void setLastName();
        void setFirstName();
    }

    interface ConstraintDescriptor {
        void copyTo(ConstraintBuilder builder);
    }

    List<CustomerProjection> getCustomersThatSatisfy(ConstraintDescriptor descriptor);
}

SQLBackedCustomerRepository implements CustomerRepository {
    List<CustomerProjection> getCustomersThatSatisfy(ConstraintDescriptor descriptor) {
        WhereClauseBuilder builder = new WhereClauseBuilder();
        descriptor.copyTo(builder);
        Query q = createQuery(builder.build());
        //...
     }
}

CollectionBackedCustomerRepository implements CustomerRepository {
    List<CustomerProjection> getCustomersThatSatisfy(ConstraintDescriptor descriptor) {
        PredicateBuilder builder = new PredicateBuilder();
        descriptor.copyTo(builder);
        Predicate p = builder.build();
        // ...
}

class MatchLastName implements CustomerRepository.ConstraintDescriptor {
    private final lastName;
    // ...

    void copyTo(CustomerRepository.ConstraintBuilder builder) {
        builder.setLastName(this.lastName);
    }
}

निष्कर्ष में: एक समग्र प्रदान करने और एक डीटीओ प्रदान करने के बीच का विकल्प इस बात पर निर्भर करता है कि आप उपभोक्ता से क्या उम्मीद कर रहे हैं। मेरा अनुमान प्रत्येक संदर्भ के लिए एक इंटरफ़ेस का समर्थन करने वाला एक ठोस कार्यान्वयन होगा।


यह सब अच्छा और अच्छा है, लेकिन पूछने वाला CQRS का उपयोग नहीं करता है। यदि आप सही हैं, तो उनकी समस्या न के बराबर होगी।
मेटाफ़ाइट

मुझे CQRS पर कोई ज्ञान नहीं है, जो मैंने इसके बारे में सुना है। जैसा कि मैं इसे समझता हूं, जब यह सोचकर कि अगर एग्रीगेटटीडीटीओ या प्रोग्रेसडीटीओ को वापस करना है, तो मैं प्रोग्रेसडोटीओ लौटाऊंगा। तो फिर getCustomersThatSatisfy(Specification spec)मैं खोज विकल्पों के लिए आवश्यक गुणों को सूचीबद्ध करूँगा। क्या मैं सही हो रहा हूँ?
कोडफिश

अस्पष्ट। मेरा मानना ​​है कि एग्रीगेटडेटीओ का अस्तित्व नहीं होना चाहिए। एक समुच्चय की बात यह सुनिश्चित करने के लिए है कि सभी परिवर्तन व्यावसायिक अपरिवर्तनीय को संतुष्ट करते हैं। यह डोमेन नियमों का एनकैप्सुलेशन है जिसे संतुष्ट करने की आवश्यकता है - अर्थात, यह व्यवहार है। दूसरी ओर, अनुमान, राज्य के कुछ स्नैपशॉट का प्रतिनिधित्व करते हैं जो व्यवसाय के लिए स्वीकार्य थे। विनिर्देश स्पष्ट करने के प्रयास के लिए संपादन देखें।
VoiceOfUnreason 4

मैं VoiceOfUnreason से सहमत हूं कि एक AggregateDTO मौजूद नहीं होना चाहिए - मुझे लगता है कि ProjectionDTO केवल डेटा के लिए एक दृश्यदर्शी के रूप में है। यह कॉलर के द्वारा आवश्यक के रूप में विभिन्न स्रोतों से डेटा में खींचकर इसे आकार दे सकता है। एग्रीगेट रूट को सभी संबंधित डेटा का पूर्ण प्रतिनिधित्व होना चाहिए ताकि बचत से पहले सभी संदर्भात्मक नियमों का परीक्षण किया जा सके। यह केवल अधिक कठोर परिस्थितियों में आकार बदलता है, जैसे DB तालिका परिवर्तन या संशोधित डेटा संबंध।
ब्रैड इरबी

1

डीडीडी के मेरे ज्ञान के आधार पर आपके पास यह होना चाहिए,

public CustomerDTO GetCustomerByName(string name) { /*some code*/ }

एक भंडार में, क्या मुझे IQueryable या IEnumerable वापस करना चाहिए?

यह निर्भर करता है, आपको इस प्रश्न के लिए विभिन्न लोगों के मिश्रित विचार मिलेंगे। लेकिन मेरा व्यक्तिगत रूप से मानना ​​है कि अगर आपकी रिपॉजिटरी का उपयोग ग्राहक सेवा जैसी सेवा द्वारा किया जाएगा तो आप आईक्यूएबल का उपयोग कर सकते हैं अन्यथा IEnumerable के साथ रहें।

क्या रिपॉजिटरी को IQueryable लौटना चाहिए?

किसी सेवा या भंडार में, मुझे कुछ करना चाहिए .. GetCustomerByLastName, GetCustomerByFirstName, GetCustomerByEmail? या बस एक विधि बनाएं जो GetCustomerBy (Func predicate) की तरह कुछ है?

आपकी रिपॉजिटरी में आपके पास एक जेनेरिक फंक्शन होना चाहिए, जैसे आपने कहा है, GetCustomer(Func predicate)लेकिन आपकी सर्विस लेयर में तीन अलग-अलग तरीके हैं, ऐसा इसलिए है क्योंकि एक मौका है कि आपकी सर्विस अलग-अलग क्लाइंट्स से कॉल की जा रही है और उन्हें अलग-अलग डीटीओ की जरूरत है।

यदि आप पहले से ही अवगत नहीं हैं, तो आप सामान्य CRUD के लिए सामान्य रिपोजिटरी पैटर्न का भी उपयोग कर सकते हैं।

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