DDD एप्लीकेशन सर्विसेज और REST API के बीच वैचारिक बेमेल संबंध


20

मैं एक ऐसे एप्लिकेशन को डिजाइन करने की कोशिश कर रहा हूं जिसमें एक जटिल व्यवसाय डोमेन और एक REST API का समर्थन करने की आवश्यकता है (सख्ती से REST नहीं, लेकिन संसाधन-उन्मुख)। मुझे संसाधन-उन्मुख तरीके से डोमेन मॉडल को उजागर करने के तरीके के साथ आने में कुछ परेशानी है।

DDD में, किसी डोमेन व्यवसाय के ग्राहकों को इकाईयों और डोमेन सेवाओं द्वारा कार्यान्वित किसी भी व्यावसायिक कार्यक्षमता तक पहुँचने के लिए प्रक्रियात्मक 'एप्लीकेशन सर्विसेज' लेयर से गुजरना पड़ता है। उपयोगकर्ता इकाई को अद्यतन करने के लिए दो तरीकों के साथ एक आवेदन सेवा है उदाहरण के लिए:

userService.ChangeName(name);
userService.ChangeEmail(email);

इस एप्लिकेशन सेवा का API राज्य नहीं, आदेशों (क्रियाओं, प्रक्रियाओं) को उजागर करता है।

लेकिन अगर हमें उसी एप्लिकेशन के लिए एक RESTful API प्रदान करने की आवश्यकता है, तो एक उपयोगकर्ता संसाधन मॉडल है, जो इस तरह दिखता है:

{
name:"name",
email:"email@mail.com"
}

संसाधन-उन्मुख एपीआई राज्य को उजागर करता है , न कि आदेशों को । यह निम्नलिखित चिंताओं को जन्म देता है:

  • REST API के विरुद्ध प्रत्येक अद्यतन कार्रवाई संसाधन मॉडल पर अपडेट की जा रही गुणों के आधार पर एक या अधिक अनुप्रयोग सेवा प्रक्रिया कॉल पर मैप कर सकती है

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

  • अलग-अलग क्रम में एप्लिकेशन सेवा विधियों को कॉल करने का एक अलग प्रभाव हो सकता है, जबकि REST API ऐसा दिखता है कि कोई अंतर नहीं है (एक संसाधन के भीतर)

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

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

प्रशन:

  1. क्या इस सारी जटिलता को सिर्फ (मोटी) रीस्ट-टू-एप्स सर्विस मैपिंग लेयर द्वारा संभाला जाना चाहिए?
  2. या मुझे DDD / REST की मेरी समझ में कुछ याद आ रहा है?
  3. जटिलता के एक निश्चित (काफी कम) से अधिक डोमेन मॉडल की कार्यक्षमता को उजागर करने के लिए REST केवल व्यावहारिक नहीं हो सकता है?

3
मैं व्यक्तिगत रूप से REST को आवश्यक नहीं मानता। हालाँकि, इसमें DDD को जूता करना संभव है: infoq.com/articles/rest-api-on-cqrs programmers.stackexchange.com/questions/242884/… blog.42.nl/articles-rest-and-ddd-incompatible
डेन

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

2
@astr सरल उत्तर है कि संसाधन आपके मॉडल नहीं हैं, इसलिए संसाधन हैंडलिंग कोड का डिज़ाइन आपके मॉडल के डिज़ाइन को प्रभावित नहीं करना चाहिए। संसाधन सिस्टम का एक बाहरी रूप से सामना करने वाला पहलू है, जहां मॉडल आंतरिक है। संसाधनों के बारे में उसी तरह सोचें जैसे आप यूआई के बारे में सोच सकते हैं। एक उपयोगकर्ता UI पर एक बटन पर क्लिक कर सकता है और मॉडल में सौ अलग-अलग चीजें होती हैं। एक संसाधन के समान। एक ग्राहक एक संसाधन (एक एकल विवरण) और एक लाख अलग-अलग चीजों को मॉडल में अपडेट करता है। यह आपके संसाधनों को बारीकी से अपने मॉडल को युगल करने के लिए एक विरोधी पैटर्न है।
कॉर्मैक मुलहेल

1
यह आपके डोमेन में होने वाली क्रियाओं के बारे में एक अच्छी बात है, REST राज्य परिवर्तनों के दुष्प्रभाव के रूप में, अपने डोमेन और वेब को अलग रखते हुए (रसदार बिट के लिए 25 मिनट तक आगे) yow.eventer.com/events/1004/talks/1047
कॉर्मैक मुल्हल

1
मैं रोबोट / राज्य मशीन के रूप में पूरे "उपयोगकर्ता के बारे में निश्चित नहीं हूं" मुझे लगता है कि हमें अपने उपयोगकर्ता इंटरफेस को उससे कहीं अधिक स्वाभाविक बनाने का प्रयास करना चाहिए ...
guillaume31

जवाबों:


10

मैं एक ही समस्या है और "हल" यह REST संसाधनों को अलग तरीके से मॉडलिंग करके, जैसे:

/users/1  (contains basic user attributes) 
/users/1/email 
/users/1/activation 
/users/1/address

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

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

यह HATEOAS दृष्टिकोण के लिए भी अच्छा है, क्योंकि आपके अधिक सुव्यवस्थित संसाधन इस बात की अधिक जानकारी देते हैं कि आप उनके साथ क्या कर सकते हैं (इसके बजाय क्लाइंट और सर्वर दोनों पर यह तर्क है क्योंकि यह आसानी से संसाधनों में प्रतिनिधित्व नहीं किया जा सकता है)।

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

इसके अलावा, संसाधनों का यह विभाजन कभी-कभी आसान या स्पष्ट नहीं होता है। मैं इसकी जटिलताओं के प्रबंधन के लिए मुख्य रूप से जटिल व्यवहार / जीवन चक्र के साथ संसाधनों पर ऐसा करता हूं।


यही कारण है कि मैं भी सोच रहा था - अधिक दानेदार संसाधन प्रतिनिधित्व बनाएं क्योंकि वे लेखन कार्यों के लिए अधिक सुविधाजनक हैं। जब आप इतने बारीक हो जाते हैं तो संसाधनों की क्वेरी को कैसे संभालते हैं? केवल पढ़ने के लिए डी-सामान्यीकृत प्रतिनिधित्व बनाएं?
astreltsov

1
नहीं, मेरे पास केवल-डी-सामान्यीकृत प्रतिनिधित्व नहीं है। मैं jsonapi.org मानक का उपयोग करता हूं और इसमें दिए गए संसाधन की प्रतिक्रिया में संबंधित संसाधनों को शामिल करने के लिए एक तंत्र है। मूल रूप से मैं कहता हूं "मुझे आईडी 1 के साथ उपयोगकर्ता दें और इसके उप-स्रोत ईमेल और सक्रियण भी शामिल करें"। यह उप-स्रोतों के लिए अतिरिक्त REST कॉल से छुटकारा पाने में मदद करता है और यदि आप कुछ अच्छे JSON API क्लाइंट लाइब्रेरी का उपयोग करते हैं तो यह सब-स्त्रोतों से निपटने वाले क्लाइंट की जटिलता को प्रभावित नहीं करता है।
qbd

तो सर्वर पर एक एकल GET अनुरोध एक या एक से अधिक वास्तविक प्रश्नों (कितने उप-संसाधन शामिल हैं पर निर्भर करता है) में अनुवाद करता है जो तब एकल संसाधन ऑब्जेक्ट में संयुक्त होते हैं?
astreltsov

क्या होगा यदि घोंसले के एक से अधिक स्तर आवश्यक हैं?
एस्टलरट्सोव

हां, रिलेशनल डीबीएस में यह संभवत: कई प्रश्नों का अनुवाद करेगा। महत्वाकांक्षी घोंसले का शिकार JSON API द्वारा समर्थित है, यह यहां वर्णित है: jsonapi.org/format/#fetching-includes
qbd

0

यहां महत्वपूर्ण मुद्दा यह है कि जब एक REST कॉल किया जाता है तो व्यावसायिक तर्क को पारदर्शी तरीके से कैसे लागू किया जाता है? यह एक ऐसी समस्या है जिसे REST द्वारा सीधे संबोधित नहीं किया जाता है।

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

उपरोक्त उदाहरण का उपयोग करते हुए, हम एक व्यापार तर्क पद्धति को मान्य कर सकते हैं जिसे वैशिष्ट्य कहा जाता है जब REST का उपयोग करके नाम फ़ील्ड बदल दिया जाता है:

class User { 
      String name;
      String email;

      /**
       * This method will be transparently invoked when the value of name is changed
       * by REST.
       * The XorUpdate annotation becomes effective for PUT/POST actions
       */
      @XorPostChange
      public void validateName() {
        if(name == null) {
          throw new IllegalStateException("Name cannot be set as null");
        }
      }
    }

अपने निपटान में इस तरह के एक उपकरण के साथ, आपको बस अपने व्यापार तर्क के तरीकों को उचित रूप से एनोटेट करना होगा।


0

मुझे संसाधन-उन्मुख तरीके से डोमेन मॉडल को उजागर करने के तरीके के साथ आने में कुछ परेशानी है।

आपको डोमेन मॉडल को संसाधन उन्मुख तरीके से उजागर नहीं करना चाहिए। आपको संसाधन उन्मुख तरीके से एप्लिकेशन को उजागर करना चाहिए।

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

बिल्कुल नहीं - डोमेन मॉडल के साथ इंटरफेस करने वाले एप्लिकेशन संसाधनों को कमांड भेजें।

REST API के विरुद्ध प्रत्येक अद्यतन कार्रवाई संसाधन मॉडल पर अपडेट की जा रही गुणों के आधार पर एक या अधिक अनुप्रयोग सेवा प्रक्रिया कॉल पर मैप कर सकती है

हां, हालांकि इसमें वर्तनी के लिए थोड़ा अलग तरीका है जो चीजों को सरल बना सकता है; एक या अधिक समुच्चय के लिए आदेश भेजता है जो एक प्रक्रिया के लिए एक अन्य के लिए नक्शे के खिलाफ प्रत्येक अद्यतन ऑपरेशन।

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

आप यहाँ गलत पूंछ का पीछा कर रहे हैं।

कल्पना करें: REST को पूरी तरह से चित्र से बाहर निकालें। इसके बजाय कल्पना करें कि आप इस एप्लिकेशन के लिए एक डेस्कटॉप इंटरफ़ेस लिख रहे थे। चलिए आगे कल्पना करते हैं कि आपके पास वास्तव में अच्छी डिजाइन आवश्यकताएं हैं, और कार्य आधारित यूआई को लागू कर रहे हैं। इसलिए उपयोगकर्ता को एक न्यूनतम इंटरफ़ेस मिलता है जो उस कार्य के लिए पूरी तरह से तैयार है जो वे काम कर रहे हैं; उपयोगकर्ता कुछ इनपुट निर्दिष्ट करता है फिर "VERB!" बटन।

अब क्या हुआ? उपयोगकर्ता के दृष्टिकोण से, यह एक एकल परमाणु कार्य किया जाना है। DomainModel के नजरिए से, यह कई कमांड हैं जो समुच्चय द्वारा चलाए जा रहे हैं, जहां प्रत्येक कमांड एक अलग लेनदेन में चलाया जाता है। वे पूरी तरह से असंगत हैं! खाई को पाटने के लिए हमें बीच में कुछ चाहिए!

कुछ "अनुप्रयोग" है।

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

संभावित परिणाम - आदेश सभी सफलतापूर्वक चलते हैं - भ्रष्टाचार-विरोधी परत संदेश को खारिज कर देती है - कुछ आदेश सफलतापूर्वक चलते हैं, लेकिन फिर एक समुच्चय शिकायत करता है, और आपको शमन करने के लिए एक आकस्मिकता मिली है।

अब, कल्पना करें कि आपके पास वह एप्लिकेशन निर्मित है; आप इसके साथ एक दिलचस्प तरीके से कैसे बातचीत करते हैं?

  1. हाइपरमीडिया नियंत्रण सहित क्लाइंट इसकी वर्तमान स्थिति (यानी: कार्य आधारित UI) के हाइपरमीडिया विवरण के साथ शुरू होता है।
  2. क्लाइंट कार्य का प्रतिनिधित्व (यानी: DTO) संसाधन के लिए भेजता है।
  3. संसाधन आने वाले HTTP अनुरोध को पार्स करता है, प्रतिनिधित्व को पकड़ता है, और इसे एप्लिकेशन को सौंप देता है।
  4. आवेदन कार्य चलाता है; संसाधन के दृष्टिकोण से, यह एक ब्लैक बॉक्स है जिसमें निम्नलिखित परिणामों में से एक है
    • अनुप्रयोग सफलतापूर्वक सभी समुच्चय को अद्यतन करता है: संसाधन ग्राहक को सफलता की रिपोर्ट करता है, इसे एक नए एप्लिकेशन स्थिति में निर्देशित करता है
    • एंटी-करप्शन लेयर संदेश को अस्वीकार करता है: संसाधन क्लाइंट को एक 4xx त्रुटि (शायद खराब अनुरोध) की रिपोर्ट करता है, संभवतः सामना की गई समस्या के विवरण के साथ गुजर रहा है।
    • अनुप्रयोग कुछ समुच्चय को अद्यतन करता है: संसाधन ग्राहक को रिपोर्ट करता है जिसे कमांड स्वीकार किया गया था, और ग्राहक को एक संसाधन के लिए निर्देशित करता है जो कमांड की प्रगति का प्रतिनिधित्व प्रदान करेगा।

स्वीकार किया गया सामान्य अनुप्रयोग है जब ग्राहक को जवाब देने के बाद जब तक एक अतुल्यकालिक आदेश को स्वीकार नहीं किया जाता है, तब तक संदेश को संसाधित करना स्थगित करना सामान्य है। लेकिन यह इस मामले के लिए भी अच्छी तरह से काम करता है, जहां एक ऑपरेशन जिसे परमाणु माना जाता है उसे शमन की आवश्यकता होती है।

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

में , काफी किसी भी समय आप एक से अधिक आदेशों समन्वय कर रहे हैं, तो आप (, गाथा उर्फ व्यापार प्रक्रिया उर्फ) एक प्रक्रिया के संदर्भ में सोच रहे होंगे करना चाहते हैं।

रीड मॉडल में एक समान वैचारिक बेमेल है। फिर से, कार्य आधारित इंटरफ़ेस पर विचार करें; यदि कार्य में कई समुच्चय को संशोधित करने की आवश्यकता होती है, तो कार्य को तैयार करने के लिए UI में संभवतः कई समुच्चय के डेटा शामिल हैं। यदि आपकी संसाधन योजना समुच्चय के साथ 1: 1 है, तो व्यवस्था करना मुश्किल होगा; इसके बजाय, एक संसाधन प्रदान करें जो हाइपरमीडिया नियंत्रण के साथ कई समुच्चय से डेटा का प्रतिनिधित्व लौटाता है, जो ऊपर दिए गए चर्चा के अनुसार कार्य समाप्ति के संबंध में "कार्य प्रारंभ" से संबंधित है।

इसे भी देखें: जिम वेबर द्वारा रीस्ट इन प्रैक्टिस


यदि हम अपने उपयोग के मामलों के अनुसार हमारे डोमेन के साथ बातचीत करने के लिए एपीआई डिजाइन कर रहे हैं .. तो इस तरह से चीजों को डिजाइन क्यों नहीं किया जाता है कि सगा की आवश्यकता नहीं है? हो सकता है कि मुझे कुछ याद आ रहा हो, लेकिन आपकी प्रतिक्रिया को पढ़कर मुझे विश्वास है कि REST DDD के साथ अच्छा मेल नहीं है और यह दूरस्थ प्रक्रियाओं (RPC) का उपयोग करने के लिए बेहतर है। DDD व्यवहार-केंद्रित है जबकि REST http-verb केंद्रित है। क्यों नहीं REST को चित्र से हटाया जाए और API में व्यवहार (आदेशों) को उजागर किया जाए? आखिरकार, शायद वे उपयोग के मामलों को संतुष्ट करने के लिए डिज़ाइन किए गए थे और संभवतया ट्रांजेक्शनल हैं। अगर हम UI के मालिक हैं, तो REST का क्या फायदा है?
इबेरोडेव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.