अनुप्रयोग या डोमेन सेवा में DDD रिपॉजिटरी


29

मैं इन दिनों DDD का अध्ययन कर रहा हूं, और मैं कुछ प्रश्न पूछ रहा हूं कि DDD के साथ रिपॉजिटरी कैसे प्रबंधित करें।

दरअसल, मैं दो कब्बियों से मिला हूं:

पेहला

मेरे द्वारा पढ़ी गई सेवाओं का पहला तरीका एक रिपॉजिटरी और एक डोमेन मॉडल को एप्लिकेशन सेवा में इंजेक्ट करना है।

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

यह करने का एक सरल तरीका हो सकता है:

class ApplicationService{

  constructor(domainService, repository){
    this.domainService = domainService
    this.repository = repository
  }

  postAction(data){
    if(this.domainService.validateRules(data)){
      this.repository.persist(new Entity(data.name, data.surname))
    }
    // ...
  }

}

दूसरा

दूसरी संभावना इसके बजाय डोमेन सेवा के अंदर भंडार को इंजेक्ट करना है, और केवल डोमेन सेवा के माध्यम से भंडार का उपयोग करना है:

class ApplicationService{

  constructor(domainService){
    this.domainService = domainService
  }

  postAction(data){
    if(this.domainService.persist(data)){
      console.log('all is good')
    }
    // ...
  }

}

class DomainService{

  constructor(repository){
    this.repository = repository
  }

  persist(data){
    if(this.validateRules(data)){
      this.repository.save(new Entity(data.name))
    }
  }

  validateRules(data){
    // returns a rule matching
  }

}

अब से, मैं यह भेद करने में सक्षम नहीं हूं कि कौन सा सबसे अच्छा है (यदि कोई सबसे अच्छा है) या वे दोनों को उनके संदर्भ में क्या कहते हैं।

क्या आप मुझे उदाहरण दे सकते हैं कि एक दूसरे से बेहतर और क्यों हो सकता है?



"एक आवेदन सेवा में एक रिपॉजिटरी और एक डोमेन मॉडल को इंजेक्ट करने के लिए।" कहीं एक "डोमेन मॉडल" इंजेक्ट करने से आपका क्या मतलब है? डीडीडी डोमेन मॉडल के संदर्भ में AFAICT का अर्थ है डोमेन से अवधारणाओं का पूरा सेट और उन दोनों के बीच बातचीत जो अनुप्रयोग के लिए प्रासंगिक हैं। यह एक अमूर्त चीज है, यह कुछ इन-मेमोरी ऑब्जेक्ट नहीं है। आप इसे इंजेक्ट नहीं कर सकते।
अलेक्सई

जवाबों:


31

संक्षिप्त उत्तर है - आप किसी एप्लिकेशन सेवा या डोमेन सेवा से रिपॉजिटरी का उपयोग कर सकते हैं - लेकिन यह विचार करना महत्वपूर्ण है कि आप क्यों और कैसे कर रहे हैं।

एक डोमेन सेवा का उद्देश्य

डोमेन सेवाओं को डोमेन अवधारणाओं / तर्क को बदलना चाहिए - जैसे, डोमेन सेवा विधि:

domainService.persist(data)

एक डोमेन सेवा से संबंधित नहीं है, जैसा persistकि सर्वव्यापी भाषा का हिस्सा नहीं है और दृढ़ता का संचालन डोमेन व्यवसाय तर्क का हिस्सा नहीं है।

आम तौर पर, डोमेन सेवाएँ तब उपयोगी होती हैं जब आपके पास व्यावसायिक नियम / तर्क होते हैं जिन्हें एक से अधिक समुच्चय के साथ समन्वय या काम करने की आवश्यकता होती है। यदि तर्क केवल एक समुच्चय को शामिल करता है, तो यह उस समुच्चय की संस्थाओं पर एक विधि में होना चाहिए।

अनुप्रयोग सेवाओं में भंडार

तो इस अर्थ में, आपके उदाहरण में, मैं आपका पहला विकल्प पसंद करता हूं - लेकिन यहां तक ​​कि सुधार की भी गुंजाइश है, क्योंकि आपकी डोमेन सेवा एपीआई से कच्चे डेटा को स्वीकार कर रही है - डोमेन सेवा को संरचना के बारे में क्यों जानना चाहिए data? इसके अलावा, डेटा केवल एकल एग्रीगेट से संबंधित प्रतीत होता है, इसलिए इसके लिए एक डोमेन सेवा का उपयोग करने में सीमित मूल्य है - आमतौर पर मैं सत्यापन को यूनिट कंस्ट्रक्टर के अंदर रखूंगा। जैसे

postAction(data){

  Entity entity = new Entity(data.name, data.surname);

  this.repository.persist(entity);

  // ...
}

और अगर यह अमान्य है तो एक अपवाद फेंक दें। आपके आवेदन ढांचे के आधार पर, अपवाद को पकड़ने के लिए एक सुसंगत तंत्र का होना और इसे एपि प्रकार के लिए उपयुक्त प्रतिक्रिया के लिए मैप करना सरल हो सकता है - उदाहरण के लिए एक अन्य री के लिए, 400 स्थिति कोड लौटाएं।

डोमेन सेवाओं में भंडार

उपरोक्त के बावजूद, कभी-कभी किसी डोमेन सेवा में एक रिपॉजिटरी को इंजेक्ट करना और उसका उपयोग करना उपयोगी होता है, लेकिन केवल तब जब आपकी रिपॉजिटरी को ऐसे लागू किया जाता है कि वे केवल मूल जड़ों को स्वीकार करते हैं और वापस लौटाते हैं, और यह भी कि जहाँ आप एब्रीडेक्ट कर रहे हैं जिसमें एग्रीगेट शामिल हैं। जैसे

postAction(data){

  this.domainService.doSomeBusinessProcess(data.name, data.surname, data.otherAggregateId);

  // ...
}

डोमेन सेवा का कार्यान्वयन इस तरह दिखेगा:

doSomeBusinessProcess(name, surname, otherAggregateId) {

  OtherEntity otherEntity = this.otherEntityRepository.get(otherAggregateId);

  Entity entity = this.entityFactory.create(name, surname);

  int calculationResult = this.someCalculationMethod(entity, otherEntity);

  entity.applyCalculationResultWithBusinessMeaningfulName(calculationResult);

  this.entityRepository.add(entity);

}

निष्कर्ष

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

लेकिन एक डोमेन सेवा को जोड़ना जो एक रिपॉजिटरी को लपेटता है, जिसे एक विधि कहा जाता है, जिसमें persistबहुत कम मूल्य होता है।

उस आधार पर, यदि आपकी एप्लिकेशन सेवा एक उपयोग के मामले को व्यक्त कर रही है, जो केवल एक ही एग्रीगेट के साथ काम करने के लिए कहता है, तो एप्लिकेशन सेवा से सीधे रिपॉजिटरी का उपयोग करने में कोई समस्या नहीं है।


ठीक है, इसलिए यदि मैं व्यावसायिक नियम (विनिर्देश पैटर्न नियमों को मानता हूं), यदि यह केवल एक इकाई की चिंता करता है, तो मुझे उस इकाई में सत्यापन करना चाहिए? उपयोगकर्ता इकाई के अंदर एक अच्छे उपयोगकर्ता मेल प्रारूप को नियंत्रित करने जैसे व्यावसायिक नियमों को इंजेक्ट करना अजीब लगता है। है ना? वैश्विक प्रतिक्रिया के बारे में, धन्यवाद। यह पाया गया कि "लागू करने के लिए कोई डिफ़ॉल्ट नियम" नहीं है, और यह वास्तव में हमारे usecases पर निर्भर करता है। मुझे इस काम को अच्छी तरह से
भेदने के

2
स्पष्ट करने के लिए, इकाई में जो नियम होते हैं, वे केवल वे नियम होते हैं जो उस इकाई की जिम्मेदारी होते हैं। मैं मानता हूं, एक अच्छा उपयोगकर्ता ईमेल प्रारूप को नियंत्रित करना ऐसा महसूस नहीं करता है कि यह उपयोगकर्ता इकाई में है। व्यक्तिगत रूप से, मैं एक वैल्यू ऑब्जेक्ट में एक ईमेल पते का प्रतिनिधित्व करने वाले वैल्यूएशन नियमों को रखना पसंद करता हूं। उपयोगकर्ता के पास टाइप की एक संपत्ति होगी EmailAddress, और EmailAddress निर्माणकर्ता एक स्ट्रिंग को स्वीकार करता है, और एक अपवाद को फेंकता है यदि स्ट्रिंग आवश्यक प्रारूप से मेल नहीं खाती है। फिर आप अन्य संस्थाओं पर EmailAddress ValueObject का फिर से उपयोग कर सकते हैं जिन्हें ईमेल पते को संग्रहीत करने की आवश्यकता है।
क्रिस साइमन

ठीक है, मैं देखता हूं कि अब वैल्यू ऑब्जेक्ट का उपयोग क्यों करना है। लेकिन इसका मतलब यह है कि मूल्य वस्तु एक संपत्ति है जो व्यापार नियम प्रारूप का प्रबंधन करती है?
mfrachet

1
मान ऑब्जेक्ट अपरिवर्तनीय होना चाहिए। आम तौर पर इसका मतलब है कि आप कंस्ट्रक्टर में इनिशियलाइज़ और वैरिफाई करते हैं, और किसी भी प्रॉपर्टी के लिए पब्लिक गेट / प्राइवेट सेट पैटर्न का इस्तेमाल करते हैं। लेकिन आप समानता, ToString प्रक्रिया आदि को परिभाषित करने के लिए भाषा निर्माण का उपयोग कर सकते हैं, जैसे kacper.gunia.me/ddd-building-blocks-in-php-value-object या github.com/spring-project/spring-gemfire-examples/ बूँद / मास्टर /…
क्रिस साइमन

अंत में धन्यवाद @ChrisSimon, और वास्तविक जीवन DDD स्थिति का जवाब देना जिसमें कोड शामिल है और न केवल सिद्धांत। मैंने एसओ और वेब को एक एग्रीगेट के निर्माण और बचत के कार्यात्मक उदाहरण के लिए 5 दिनों तक फँसाया है, और यह सबसे स्पष्ट स्पष्टीकरण है जो मैंने पाया है।
e_i_pi

2

स्वीकृत उत्तर के साथ समस्या है:

डोमेन मॉडल को रिपॉजिटरी पर निर्भर करने की अनुमति नहीं है और डोमेन सेवा डोमेन मॉडल का हिस्सा है -> डोमेन सेवा रिपॉजिटरी पर निर्भर नहीं होनी चाहिए।

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

आपके उदाहरण के आधार पर यह इस तरह दिख सकता है:

class ApplicationService{

  constructor(domainService, repository){
    this.domainService = domainService
    this.repositoryA = repositoryA
    this.repositoryB = repositoryB
    this.repositoryC = repositoryC
  }

  // any parsing and/or pre-business validation already happened in controller or whoever is a caller
  executeUserStory(data){
    const entityA = this.repositoryA.get(data.criterionForEntityA)
    const entityB = this.repositoryB.get(data.criterionForEntityB)

    if(this.domainService.validateSomeBusinessRules(entityA, entityB)){
      this.repositoryC.persist(new EntityC(entityA.name, entityB.surname))
    }
    // ...
  }
}

इसलिए, अंगूठे का नियम: डोमेन मॉडल बाहरी परतों पर निर्भर नहीं करता है

इस लेख से अनुप्रयोग बनाम डोमेन सेवा :

  • डोमेन सेवाएं बहुत बारीक होती हैं, जहां एप्लिकेशन सेवाएं एक एपीआई प्रदान करने के साथ शुद्ध होती हैं।

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

  • डोमेन सेवा विधियों में ऑपरेंड और रिटर्न वैल्यू के रूप में अन्य डोमेन तत्व हो सकते हैं, जबकि एप्लिकेशन सेवाएं ट्रिवियल ऑपरेंड जैसे आइडेंटिटी वैल्यू और प्राइमरी डेटा संरचनाओं पर काम करती हैं।

  • अनुप्रयोग सेवाएँ डोमेन लॉजिक को निष्पादित करने के लिए आवश्यक अवसंरचनात्मक सेवाओं पर निर्भरता की घोषणा करती हैं।


1

जब तक आपकी सेवाएं और ऑब्जेक्ट ज़िम्मेदारी के कुछ सुसंगत सेट को अतिक्रमण नहीं करते, तब तक आपके पैटर्न अच्छे नहीं हैं।

सबसे पहले यह कहें कि आपका डोमेन ऑब्जेक्ट क्या है और इस बारे में बात करें कि यह डोमेन भाषा में क्या कर सकता है। यदि यह वैध या अमान्य हो सकता है तो डोमेन डोमेन की संपत्ति के रूप में ऐसा क्यों नहीं है?

यदि उदाहरण के लिए वस्तुओं की वैधता केवल किसी अन्य वस्तु के संदर्भ में समझ में आती है, तो शायद आपके पास एक जिम्मेदारी है 'डोमेन ऑब्जेक्ट्स के लिए सत्यापन नियम X' जो सेवाओं के एक सेट में संलग्न हो सकता है।

क्या किसी वस्तु को मान्य करना आपके व्यावसायिक नियमों के भीतर इसे संग्रहीत करने की आवश्यकता है? शायद ऩही। 'स्टोरिंग ऑब्जेक्ट्स' की जिम्मेदारी सामान्य रूप से एक अलग रिपॉजिटरी ऑब्जेक्ट में जाती है।

अब आपके पास एक ऑपरेशन है जिसे आप निष्पादित करना चाहते हैं जो कई जिम्मेदारियों को शामिल करता है, एक ऑब्जेक्ट बनाता है, इसे मान्य करें और यदि मान्य है, तो इसे स्टोर करें।

क्या यह ऑपरेशन डोमेन ऑब्जेक्ट के लिए आंतरिक है? फिर इसे उस वस्तु का हिस्सा बनाएं अर्थातExamQuestion.Answer(string answer)

क्या यह आपके डोमेन के किसी अन्य भाग के साथ फिट बैठता है? इसे वहाँ रखोBasket.Purchase(Order order)

क्या आप बल्कि ADM REST सेवाएँ करना चाहेंगे? ठीक है फिर।

Controller.Post(json) 
{ 
    parse(json); 
    verify(parsedStruct); 
    save(parsedStruct); 
    return 400;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.