design patterns - CQRS: कमांड रिटर्न वैल्यूज




design-patterns architecture (3)

अच्छा, कमांड हैंडलर मान लौटाते हैं या नहीं?

उन्हें व्यावसायिक डेटा , केवल मेटा डेटा (कमांड को निष्पादित करने की सफलता या विफलता के बारे में) नहीं लौटना चाहिए। CQRS CQS को उच्च स्तर पर ले जाया जाता है। यहां तक ​​कि अगर आप शुद्धतावादी के नियमों को तोड़ देंगे और कुछ वापस करेंगे, तो आप क्या लौटाएंगे? CQRS में कमांड हैंडलर एक application service का एक तरीका है जो aggregate लोड करता है फिर aggregate पर एक विधि को कॉल करता है फिर यह aggregate को aggregate । कमांड हैंडलर का इरादा aggregate को संशोधित करना है। आपको नहीं पता होगा कि क्या वापस लौटना है जो कॉलर से स्वतंत्र होगा। हर कमांड हैंडलर कॉलर / ग्राहक नए राज्य के बारे में कुछ और जानना चाहते हैं।

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

अन्यथा सोचें, यदि आप एक कमांड हैंडलर से कुछ वापस करते हैं तो आप इसे दो जिम्मेदारियां देते हैं: 1. समग्र स्थिति को संशोधित करें और 2. कुछ रीड-मॉडल को क्वेरी करें।

कमांड सत्यापन के संबंध में, कम से कम दो प्रकार के कमांड सत्यापन हैं:

  1. कमांड सैनिटिटी चेक, जो सत्यापित करता है कि कमांड में सही डेटा है (यानी एक ईमेल पता मान्य है); कमांड हैंडलर (एप्लिकेशन सर्विस) में या कमांड कंस्ट्रक्टर में कमांड पहुंचने से पहले यह किया जाता है;
  2. डोमेन इन्वोलिएंट्स चेक, जो कि एग्रीगेट तक पहुंचने के बाद एग्रीगेट के अंदर किया जाता है (एग्रीगेट पर मेथड को कॉल करने के बाद) और यह चेक करता है कि एग्रीगेट नए स्टेट में म्यूट कर सकता है।

हालाँकि, अगर हम कुछ लेवल ऊपर जाते हैं, तो Presentation layer (यानी REST एंडपॉइंट) में, Application layer का क्लाइंट, हम कुछ भी वापस कर सकते हैं और हम नियमों को तोड़ नहीं सकते क्योंकि उपयोग के मामलों के बाद एंडपॉइंट्स डिजाइन किए जाते हैं, आप जानते हैं वास्तव में कमांड के निष्पादित होने के बाद आप क्या उपयोग करना चाहते हैं, हर उपयोग के मामले में।

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

दुविधा

यहाँ भ्रम के उदाहरण हैं ...

  • उदी दहन का कहना है कि कमांड "क्लाइंट को त्रुटियों को वापस नहीं कर रहे हैं", लेकिन उसी लेख में वह एक आरेख दिखाता है जहां कमांड ग्राहक को वास्तव में त्रुटियां लौटाते हैं।

  • Microsoft प्रेस स्टोर के लेख में कहा गया है "कमांड ... प्रतिक्रिया नहीं देता है" लेकिन फिर एक अस्पष्ट सावधानी देने के लिए आगे बढ़ता है:

चूंकि CQRS के आसपास युद्धक्षेत्र का अनुभव बढ़ता है, कुछ प्रथाएं मजबूत होती हैं और सर्वोत्तम प्रथाएं बन जाती हैं। आंशिक रूप से जो हमने अभी कहा है, उसके विपरीत ... आज यह सोचना एक आम दृश्य है कि कमांड हैंडलर और एप्लिकेशन दोनों को यह जानने की जरूरत है कि लेन-देन का संचालन कैसे हुआ। परिणाम जानना होगा ...

अच्छा, कमांड हैंडलर मान लौटाते हैं या नहीं?

उत्तर?

जिमी बोगर्ड के " सीक्यूआरएस मिथकों " से एक क्यू लेते हुए, मुझे लगता है कि इस सवाल का जवाब (ओं) पर निर्भर करता है कि आप किस प्रोग्रामेटिक / संदर्भात्मक "चतुर्थांश" पर बात कर रहे हैं:

+-------------+-------------------------+-----------------+
|             | Real-time, Synchronous  |  Queued, Async  |
+-------------+-------------------------+-----------------+
| Acceptance  | Exception/return-value* | <see below>     |
| Fulfillment | return-value            | n/a             |
+-------------+-------------------------+-----------------+

स्वीकृति (उदाहरण के लिए सत्यापन)

कमांड "स्वीकृति" ज्यादातर सत्यापन को संदर्भित करता है। संभवतया सत्यापन परिणाम कॉल करने वाले को समान रूप से दिया जाना चाहिए, चाहे "पूर्ति" कमांड तुल्यकालिक या पंक्तिबद्ध हो या नहीं।

हालाँकि, ऐसा लगता है कि कई चिकित्सक कमांड हैंडलर के भीतर से सत्यापन शुरू नहीं करते हैं। मैंने जो देखा है, वह या तो इसलिए है क्योंकि (1) वे पहले से ही आवेदन परत पर सत्यापन को संभालने का एक शानदार तरीका ढूंढ चुके हैं (यानी ASP.NET MVC नियंत्रक डेटा एनोटेशन के माध्यम से वैध स्थिति की जाँच कर रहा है) या (2) एक वास्तुकला उस स्थान पर है जो आदेशों को एक (प्रक्रिया से बाहर) बस या कतार में प्रस्तुत करता है। अतुल्यकालिक के ये बाद के रूप आम तौर पर तुल्यकालिक सत्यापन शब्दार्थ या इंटरफेस प्रदान नहीं करते हैं।

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

पूर्ति

एक कमांड की "पूर्ति" के बारे में, जिस क्लाइंट ने कमांड जारी किया है, उसे नए बनाए गए रिकॉर्ड या शायद विफलता की जानकारी के लिए गुंजाइश_ पहचान की आवश्यकता हो सकती है - जैसे "खाता ओवररन।"

वास्तविक समय की सेटिंग में ऐसा लगता है कि एक वापसी मूल्य सबसे अधिक समझ में आता है; अपवादों का उपयोग व्यवसाय-संबंधी विफलता परिणामों को संप्रेषित करने के लिए नहीं किया जाना चाहिए। हालांकि, "कतारबद्ध" संदर्भ में ... वापसी मूल्य स्वाभाविक रूप से कोई मतलब नहीं रखते हैं।

यह वह जगह है जहाँ सभी भ्रम को संक्षेप में प्रस्तुत किया जा सकता है:

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

इसलिए, उदाहरण के लिए, मेरा मानना ​​है कि एक तुल्यकालिक (अनुरोध-प्रतिक्रिया) संदर्भ ग्रहण किया गया था जब जिमी बोगर्ड ने यह नमूना आदेश इंटरफ़ेस प्रदान किया था:

public interface ICommand<out TResult> { }

public interface ICommandHandler<in TCommand, out TResult>
    where TCommand : ICommand<TResult>
{
    TResult Handle(TCommand command);
}

उनका Mediatr उत्पाद, आखिरकार, एक इन-मेमोरी टूल है। यह सब देखते हुए, मुझे लगता है कि कारण यह है कि जिमी ने सावधानीपूर्वक एक कमांड से शून्य वापसी का समय लिया था क्योंकि "कमांड हैंडलर को रिटर्न मान नहीं होना चाहिए," बल्कि इसलिए कि वह बस चाहता था कि उसका मध्यस्थ वर्ग एक सुसंगत इंटरफ़ेस रखे:

public interface IMediator
{
    TResponse Request<TResponse>(IQuery<TResponse> query);
    TResult Send<TResult>(ICommand<TResult> query);  //This is the signature in question.
}

... भले ही सभी आदेशों को वापस करने का सार्थक मूल्य नहीं है।

दोहराएँ और लपेटें

क्या मैं सही ढंग से कैप्चर कर रहा हूं कि इस विषय पर भ्रम क्यों है? क्या मुझे कुछ याद आ रहा है?


@Constantin Galbenu के लिए उत्तर दें, मुझे सीमा का सामना करना पड़ा।

@Misanthrope और वास्तव में आप उन घटनाओं के साथ क्या करते हैं?

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

यह अत्यंत उपयोगी है जब:

  1. आपको अपवादों के बजाय घटनाओं के माध्यम से त्रुटि के बारे में सूचित करने की आवश्यकता है। यह आमतौर पर तब होता है जब आपके मॉडल को सहेजने की आवश्यकता होती है (उदाहरण के लिए, यह गलत कोड / पासवर्ड के साथ प्रयासों की संख्या की गणना करता है) भले ही त्रुटि हुई हो। इसके अलावा, कुछ लोग व्यावसायिक त्रुटियों के अपवादों का उपयोग नहीं करते हैं - केवल ईवेंट ( http://andrzejonsoftware.blogspot.com/2014/06/custom-exceptions-or-domain-events.html ) कोई विशेष कारण नहीं है यह सोचने के लिए कि कमांड हैंडलर से व्यावसायिक अपवादों को फेंकना ठीक है, लेकिन डोमेन घटनाओं को वापस करना नहीं है
  2. जब घटना आपके कुल रूट के अंदर केवल कुछ परिस्थितियों के साथ होती है।

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

StrangersWereMatched वास्तव में अभी हुआ तो प्रतिक्रिया की जाँच इतनी सीधी है:

$events = $this->commandBus->handle(new LikeStranger(...));

if ($events->contains(StrangersWereMatched::class)) {
  return LikeApiResponse::matched();
} else {
  return LikeApiResponse::unknown();
}

हां, आप उदाहरण के लिए कमांड आईडी पेश कर सकते हैं, और इसे बनाए रखने के लिए मैच रीड मॉडल बना सकते हैं:

// ...

$commandId = CommandId::generate();

$events = $this->commandBus->handle(
  $commandId,
  new LikeStranger($strangerWhoLikesId, $strangerId)
);

$match = $this->matchQueryService->find($strangerWhoLikesId, $strangerId);

if ($match->isResultOfCommand($commandId)) {
  return LikeApiResponse::matched();
} else {
  return LikeApiResponse::unknown();
}

... लेकिन इसके बारे में सोचें: आपको क्यों लगता है कि सीधा तर्क के साथ पहला उदाहरण बदतर है? यह वैसे भी CQRS का उल्लंघन नहीं करता है, मैंने सिर्फ निहित स्पष्ट किया है। यह सांविधिक अपरिवर्तनीय दृष्टिकोण है। बग से टकराने की कम संभावना (उदाहरण के लिए, यदि matchQueryService से कैश की जाती है / देरी हो रही है [तुरंत संगत नहीं], तो आपको समस्या होती है)।

हां, जब मिलान का तथ्य पर्याप्त नहीं है और आपको प्रतिक्रिया के लिए डेटा प्राप्त करने की आवश्यकता है, तो आपको क्वेरी सेवा का उपयोग करना होगा। लेकिन कमांड हैंडलर से घटनाओं को प्राप्त करने के लिए कुछ भी नहीं रोकता है।


व्लादिक खोनोनोव द्वारा CQRS में टैकलिंग कॉम्प्लेक्सिटी में सलाह के बाद कमांड हैंडलिंग अपने परिणाम से संबंधित जानकारी वापस कर सकता है।

किसी भी [CQRS] सिद्धांतों का उल्लंघन किए बिना, एक कमांड निम्नलिखित डेटा को सुरक्षित रूप से वापस कर सकता है:

  • निष्पादन परिणाम: सफलता या विफलता;
  • विफलता के मामले में त्रुटि संदेश या सत्यापन त्रुटियां;
  • सफलता के मामले में कुल मिलाकर नया संस्करण;

यह जानकारी आपके सिस्टम के उपयोगकर्ता अनुभव में नाटकीय रूप से सुधार करेगी, क्योंकि:

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

डैनियल व्हिटकर एक कमांड हैंडलर से " कॉमन रिजल्ट " ऑब्जेक्ट लौटाने की वकालत करते हैं जिसमें यह जानकारी होती है।





command-pattern