ASP.NET अनुप्रयोग विकसित करते समय CQRS / MediatR इसके लायक है?


17

मैं हाल ही में CQRS / MediatR में देख रहा हूं। लेकिन जितना अधिक मैं इसे पसंद करता हूं उतना कम मैं इसे नीचे ड्रिल करता हूं। शायद मैंने कुछ / सब कुछ गलत समझा है।

तो यह आपके नियंत्रक को इस तक कम करने का दावा करके भयानक शुरू होता है

public async Task<ActionResult> Edit(Edit.Query query)
{
    var model = await _mediator.SendAsync(query);

    return View(model);
}

जो पतली नियंत्रक दिशानिर्देश के साथ पूरी तरह से फिट बैठता है। हालाँकि यह कुछ महत्वपूर्ण महत्वपूर्ण विवरणों को छोड़ देता है - त्रुटि से निपटने।

Loginनए MVC प्रोजेक्ट से डिफ़ॉल्ट कार्रवाई को देखने देता है

public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
    ViewData["ReturnUrl"] = returnUrl;
    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            _logger.LogInformation(1, "User logged in.");
            return RedirectToLocal(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning(2, "User account locked out.");
            return View("Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return View(model);
        }
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

परिवर्तित करना जो हमें वास्तविक विश्व की समस्याओं का एक समूह प्रदान करता है। याद रखें कि लक्ष्य इसे कम करना है

public async Task<IActionResult> Login(Login.Command command, string returnUrl = null)
{
    var model = await _mediator.SendAsync(command);

    return View(model);
}

इसका एक संभावित समाधान एक के CommandResult<T>बजाय वापस लौटना modelऔर फिर CommandResultएक पोस्ट एक्शन फिल्टर में संभालना है । जैसा कि यहां चर्चा की गई है

एक कार्यान्वयन CommandResultइस तरह हो सकता है

public interface ICommandResult  
{
    bool IsSuccess { get; }
    bool IsFailure { get; }
    object Result { get; set; }
}

स्रोत

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

एक और समस्या है returnUrl। हमारे पास यह return RedirectToLocal(returnUrl);कोड है। किसी तरह हमें कमांड की सफलता की स्थिति के आधार पर सशर्त तर्कों को संभालने की आवश्यकता है। जबकि मुझे लगता है कि यह किया जा सकता है (मुझे यकीन नहीं है कि अगर मॉडलबिंडर FromBody और FromQuery ( returnUrlFromQuery से है) एक ही मॉडल के लिए तर्क दे सकता है)। कोई केवल यह सोच सकता है कि सड़क के नीचे किस तरह के पागल परिदृश्य आ सकते हैं।

लौटे त्रुटि संदेशों के साथ मॉडल सत्यापन भी अधिक जटिल हो गया है। इसे एक उदाहरण के रूप में लें

else
{
    ModelState.AddModelError(string.Empty, "Invalid login attempt.");
    return View(model);
}

हम मॉडल के साथ एक त्रुटि संदेश देते हैं। इस तरह की बात एक Exceptionरणनीति का उपयोग करके नहीं की जा सकती (जैसा कि यहां बताया गया है ) क्योंकि हमें मॉडल की आवश्यकता है। शायद आप मॉडल प्राप्त कर सकते हैं, Requestलेकिन यह एक बहुत ही शामिल प्रक्रिया होगी।

तो सब सब में मैं एक कठिन समय इस "सरल" कार्रवाई परिवर्तित कर रहा हूँ।

मुझे इनपुट्स की तलाश है। क्या मैं यहाँ पूरी तरह से गलत हूँ?


6
लगता है जैसे आप पहले से ही संबंधित चिंताओं को अच्छी तरह से समझते हैं। वहाँ बहुत सारे "सिल्वर बुलेट्स" हैं, जिनमें खिलौने के उदाहरण हैं जो उनकी उपयोगिता को साबित करते हैं, लेकिन जो वास्तविक, वास्तविक जीवन के आवेदन की वास्तविकता से निचोड़ने पर अनिवार्य रूप से खत्म हो जाते हैं।
रॉबर्ट हार्वे

मेडिएटर व्यवहार की जाँच करें। यह मूल रूप से एक पाइपलाइन है जो आपको क्रॉस-कटिंग चिंताओं से निपटने की अनुमति देता है।
fml

जवाबों:


14

मुझे लगता है कि आप अपने द्वारा उपयोग किए जा रहे पैटर्न की बहुत अधिक उम्मीद कर रहे हैं। CQRS विशेष रूप से डेटाबेस के लिए क्वेरी और कमांड के बीच मॉडल में अंतर को संबोधित करने के लिए डिज़ाइन किया गया है , और MediatR सिर्फ इन-प्रोसेस मैसेजिंग लाइब्रेरी है। CQRS व्यावसायिक तर्क की आवश्यकता को समाप्त करने का दावा नहीं करता है जैसे आप उनसे अपेक्षा कर रहे हैं। CQRS डेटा एक्सेस के लिए एक पैटर्न है, लेकिन आपकी समस्याएं प्रेजेंटेशन लेयर-redirects, views, नियंत्रकों के साथ हैं।

मुझे लगता है कि आप प्रमाणीकरण के लिए CQRS पैटर्न को गलत तरीके से लागू कर सकते हैं। लॉगिन के साथ यह CQRS में एक कमांड के रूप में मॉडलिंग नहीं कर सकता है क्योंकि

कमांड्स: एक सिस्टम की स्थिति को बदलें लेकिन एक मान वापस न करें
- मार्टिन फाउलर कमांडविकैपेशन

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

ASP.NET अनुप्रयोग विकसित करते समय CQRS / MediatR इसके लायक है?

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

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

तो आपके प्रश्न का उत्तर देने के लिए CQRS को CRUD उपयुक्त होने पर एप्लिकेशन डिज़ाइन करते समय पहला सहारा नहीं होना चाहिए। आपके प्रश्न में कुछ भी मुझे संकेत नहीं दिया कि आपके पास CQRS का उपयोग करने का एक कारण है।

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


1
मैं 100% सहमत हूँ। CQRS बस थोड़ा सा सम्मोहित है, इसलिए मैंने सोचा कि "उन्होंने" कुछ ऐसा देखा जो मैंने नहीं किया। क्योंकि मुझे CRUD वेब एप्स में CQRS के फायदों को देखते हुए कठिन समय हो रहा है। अब तक केवल परिदृश्य CQRS + ES है जो मेरे लिए समझ में आता है।
Sn --bjørn

मेरी नई नौकरी पर कुछ लोगों ने नए ASP.Net सिस्टम पर MediatR डालने का फैसला किया जो इसे एक वास्तुकला के रूप में दावा करता है। कार्यान्वयन वह बना DDD, और न ही ठोस, और न ही सूखी, और न ही KISS नहीं है। यह YAGNI से भरा एक छोटा सिस्टम है। और यह आपकी, आपकी शामिल जैसी कुछ टिप्पणियों के लंबे समय बाद शुरू हुई है। मैं यह पता लगाने की कोशिश कर रहा हूं कि मैं धीरे-धीरे अपनी वास्तुकला को अनुकूलित करने के लिए कोड को कैसे फिर से सक्रिय कर सकता हूं। एक व्यावसायिक परत के बाहर CQRS के बारे में भी मेरी यही राय थी और मुझे खुशी है कि कई अनुभवी देव इस तरह से सोच रहे हैं।
MFedatto

यह पुष्टि करने के लिए कि CQRS शामिल करने का विचार / MediatR YAGNI का एक बहुत और KISS का अभाव है, के साथ जुड़ा हो सकता है थोड़ा विडंबना है जब वास्तव में लोकप्रिय विकल्प में से कुछ, भंडार पैटर्न की तरह, YAGNI भंडार वर्ग सूजन और मजबूर को बढ़ावा देने के सभी रूट एग्रीगेट पर बहुत सारे CRUD ऑपरेशंस को निर्दिष्ट करने के लिए इंटरफेस जो इस तरह के इंटरफेस को लागू करना चाहते हैं, अक्सर उन तरीकों को छोड़कर या तो अप्रयुक्त या "लागू नहीं" अपवादों से भरा होता है। क्योंकि CQRS इन सामान्यीकरणों का उपयोग नहीं करता है, यह केवल वही लागू कर सकता है जिसकी आवश्यकता है।
लेस्सर वैल्मोंट

@LesairValmont रिपोजिटरी केवल CRUD माना जाता है। "बहुत सारे CRUD ऑपरेशन निर्दिष्ट करें" केवल 4 (या "सूची" के साथ 5) होना चाहिए। यदि आपके पास अधिक विशिष्ट क्वेरी एक्सेस पैटर्न हैं, तो वे आपके रिपॉजिटरी इंटरफ़ेस में नहीं होने चाहिए। मैंने अप्रयुक्त रिपॉजिटरी विधियों की समस्या में कभी भाग नहीं लिया है। क्या आप एक उदाहरण दे सकते हैं?
शमूएल

@ सैमुअल: मुझे लगता है कि कुछ परिदृश्यों के लिए रिपॉजिटरी पैटर्न बिल्कुल ठीक है, ठीक वैसे ही जैसे CQRS है। दरअसल, एक बड़े एप्लिकेशन पर, कुछ हिस्से होंगे जिनका सबसे अच्छा फिट रिपॉजिटरी पैटर्न और अन्य होगा जो CQRS द्वारा अधिक लाभान्वित होंगे। यह कई अलग-अलग कारकों पर निर्भर करता है, जैसे दर्शन आवेदन के उस भाग (जैसे कार्य-आधारित (CQRS) बनाम CRUD (रेपो)), ORM का उपयोग किया जा रहा है (यदि कोई हो), तो डोमेन का मॉडलिंग ( जैसे DDD)। सरल CRUD कैटलॉग के लिए CQRS निश्चित रूप से ओवरकिल है, और कुछ वास्तविक समय सहयोगी विशेषताएं (जैसे चैट) न तो उपयोग नहीं करेंगे।
लेस्सर वेलमॉन्ट सेप

10

CQRS एक डेटा प्रबंधन चीज़ के बजाय अधिक है और यह एक एप्लीकेशन लेयर (या यदि आप चाहें तो डोमेन में बहुत अधिक ब्लीड करने की प्रवृत्ति नहीं रखता है, क्योंकि यह डीडीडी सिस्टम में सबसे अधिक बार उपयोग किया जाता है)। दूसरी ओर, आपका MVC एप्लिकेशन एक प्रस्तुति परत अनुप्रयोग है और इसे CQRS की क्वेरी / दृढ़ता कोर से काफी अच्छी तरह से अलग किया जाना चाहिए।

ध्यान देने योग्य एक और बात (डिफ़ॉल्ट Loginविधि की अपनी तुलना को देखते हुए और पतले नियंत्रकों की इच्छा को देखते हुए ): मैं वास्तव में डिफ़ॉल्ट ASP.NET टेम्प्लेट / बॉयलरप्लेट कोड का पालन नहीं करूंगा क्योंकि कुछ भी हमें सर्वोत्तम प्रथाओं के बारे में चिंता करनी चाहिए।

मुझे पतले नियंत्रक भी पसंद हैं, क्योंकि वे पढ़ने में बहुत आसान हैं। प्रत्येक नियंत्रक मेरे पास आमतौर पर एक "सेवा" ऑब्जेक्ट होता है जो इसके साथ जोड़ेगा जो अनिवार्य रूप से नियंत्रक द्वारा आवश्यक तर्क को संभालता है:

public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) {

    var result = _service.Login(model);
    switch (result) {
        case result.lockout: return View("Lockout");
        case result.ok: return RedirectToLocal(returnUrl);
        default: return View("GeneralError");
    }
}

अभी भी काफी पतली है, लेकिन हमने वास्तव में नहीं बदला है कि कोड कैसे काम करता है, बस हैंडलिंग को सेवा पद्धति में सौंप दें, जो वास्तव में नियंत्रक कार्यों को पचाने में आसान बनाने के अलावा कोई उद्देश्य नहीं देता है।

ध्यान में रखते हुए, यह सेवा वर्ग तर्क / मॉडल के लिए तर्क को आवश्यक रूप से प्रस्तुत करने के लिए जिम्मेदार है, कोड को साफ रखने के लिए यह वास्तव में नियंत्रक का केवल एक मामूली विस्तार है। सेवा के तरीके आमतौर पर बहुत कम होते हैं।

मुझे यकीन नहीं है कि मध्यस्थ वैचारिक रूप से कुछ अलग कर रहे होंगे: नियंत्रक से कुछ बुनियादी नियंत्रक तर्क को स्थानांतरित करना और कहीं और संसाधित किया जाना।

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


5

मैं अत्यधिक अनुशंसा करता हूं कि आप http अनुरोध https://www.youtube.com/watch?v=SUiWfhAhgQw मॉडलिंग के लिए अपने दृष्टिकोण पर जिमी बोगर्ड की एनडीसी प्रस्तुति देखें

आपको तब स्पष्ट रूप से पता चल जाएगा कि मेडियाट्र का उपयोग किस लिए किया जाता है।

जिमी में पैटर्न और अमूर्तता का अंधा पालन नहीं है। वह बहुत व्यावहारिक है। Mediatr नियंत्रक क्रियाओं को साफ करता है। अपवाद से निपटने के लिए, मुझे लगता है कि एक अभिभावक वर्ग में Execute की तरह कुछ कहा जाता है। तो आप एक बहुत साफ नियंत्रक कार्रवाई के साथ समाप्त होते हैं।

कुछ इस तरह:

public bool Execute<T>(Func<T> messageFunction)
{
    try
    {
        messageFunction();

        return true;
    }
    catch (ValidationException exception)
    {
        Errors = string.Join(Environment.NewLine, exception.Errors.Select(e => e.ErrorMessage));
        Logger.LogException(exception, "ValidationException caught in SiteController");
    }
    catch (SiteException exception)
    {
        Errors = exception.Message;
        Logger.LogException(exception);
    }
    catch (DbEntityValidationException dbEntityValidationException)
    {
        // Retrieve the error messages as a list of strings.
        var errorMessages = dbEntityValidationException.EntityValidationErrors
                .SelectMany(x => x.ValidationErrors)
                .Select(x => x.ErrorMessage);

        // Join the list to a single string.
        var fullErrorMessage = string.Join("; ", errorMessages);

        // Combine the original exception message with the new one.
        var exceptionMessage = string.Concat(dbEntityValidationException.Message, " The validation errors are: ", fullErrorMessage);

        Logger.LogError(exceptionMessage);

        // Throw a new DbEntityValidationException with the improved exception message.
        throw new DbEntityValidationException(exceptionMessage, dbEntityValidationException.EntityValidationErrors);                
    }
    catch (Exception exception)
    {
        Errors = "An error has occurred.";
        Logger.LogException(exception, "Exception caught in SiteController.");
    }

    // used to indicate that any transaction which may be in progress needs to be rolled back for this request.
    HttpContext.Items[UiConstants.Error] = true;

    Response.StatusCode = (int)HttpStatusCode.InternalServerError; // fail

    return false;
}

उपयोग कुछ इस तरह दिखता है:

[Route("api/licence")]
public IHttpActionResult Post(LicenceEditModel licenceEditModel)
{
    var updateLicenceCommand = new UpdateLicenceCommand { LicenceEditModel = licenceEditModel };
    int licenceId = -1;

    if (Execute(() => _mediator.Send(updateLicenceCommand)))
    {
        return JsonSuccess(licenceEditModel);
    }

    return JsonError(Errors);
}

उम्मीद है की वो मदद करदे।


4

बहुत से लोग (मैंने यह भी किया था) एक पुस्तकालय के साथ पैटर्न को भ्रमित करते हैं। CQRS एक पैटर्न है लेकिन MediatR एक लाइब्रेरी है जिसका उपयोग आप उस पैटर्न को लागू करने के लिए कर सकते हैं

आप बिना CTCRS का उपयोग MediatR या किसी भी इन-प्रोसेस मैसेजिंग लाइब्रेरी के बिना कर सकते हैं और आप CQRS के बिना MediatR का उपयोग कर सकते हैं:

public interface IProductsWriteService
{
    void CreateProduct(CreateProductCommand createProductCommand);
}

public interface IProductsReadService
{
    ProductDto QueryProduct(Guid guid);
}

CQS इस तरह दिखेगा:

public interface IProductsService
{
    void CreateProduct(CreateProductCommand createProductCommand);
    ProductDto QueryProduct(Guid guid);
}

वास्तव में, आपको ऊपर की तरह अपने इनपुट मॉडल "कमांड" का नाम नहीं देना होगा CreateProductCommand। और आपके प्रश्नों का इनपुट "प्रश्न"। कमांड और क्वेश्चन मेथड हैं, मॉडल नहीं।

CQRS ज़िम्मेदारी के अलगाव के बारे में है (पढ़ें विधियों को लिखने के तरीकों से अलग जगह में होना चाहिए - पृथक)। यह CQS का विस्तार है लेकिन अंतर CQS में है आप इन विधियों को 1 कक्षा में रख सकते हैं। (कोई जिम्मेदारी अलगाव, सिर्फ कमांड-क्वेरी अलगाव)। पृथक्करण बनाम अलगाव देखें

से https://martinfowler.com/bliki/CQRS.html :

इसके दिल में यह धारणा है कि आप सूचना को पढ़ने के लिए उपयोग किए जाने वाले मॉडल की तुलना में जानकारी को अपडेट करने के लिए एक अलग मॉडल का उपयोग कर सकते हैं।

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

CQRS और आईडी जनरेशन लिमिटेशन

CQRS या CQS का उपयोग करते समय आपके सामने एक सीमा होगी

मूल विवरण कमांड में तकनीकी रूप से किसी भी मूल्य (शून्य) को वापस नहीं करना चाहिए, जो मुझे बेवकूफ लगता है क्योंकि नव निर्मित वस्तु से उत्पन्न आईडी प्राप्त करने का कोई आसान तरीका नहीं है: /programming/4361889/how-to- get-id-in-create-when-apply-cqrs

इसलिए आपको डेटाबेस को करने देने के बजाय प्रत्येक बार खुद आईडी बनाना होगा।


यदि आप अधिक जानना चाहते हैं: https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf


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

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

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