CQRS / MediatR นั้นคุ้มค่าหรือไม่เมื่อพัฒนาแอปพลิเคชัน ASP.NET


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แต่นั่นเป็นการเริ่มต้นที่ยอดเยี่ยมสำหรับคลาส / อินเทอร์เฟซที่ป่องมาก อาจมีคนบอกว่ามันไม่สอดคล้องกับ Single Responsibility (SRP)

returnUrlปัญหาอีกประการหนึ่งคือ เรามีreturn RedirectToLocal(returnUrl);รหัสชิ้นนี้ ยังไงก็เถอะเราจำเป็นต้องจัดการข้อโต้แย้งตามเงื่อนไขขึ้นอยู่กับสถานะความสำเร็จของคำสั่ง ในขณะที่ฉันคิดว่าสามารถทำได้ (ฉันไม่แน่ใจว่า ModelBinder สามารถแมป FromBody และ FromQuery ( returnUrlคือ FromQuery) กับรูปแบบเดียว) มีเพียงคนเดียวที่สงสัยว่าสถานการณ์เลวร้ายแบบไหนที่จะเกิดขึ้นได้

การตรวจสอบรูปแบบมีความซับซ้อนมากขึ้นพร้อมกับส่งคืนข้อความแสดงข้อผิดพลาด ใช้สิ่งนี้เป็นตัวอย่าง

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

เราแนบข้อความแสดงข้อผิดพลาดพร้อมกับรุ่น สิ่งนี้ไม่สามารถทำได้โดยใช้Exceptionกลยุทธ์ (ตามที่แนะนำไว้ที่นี่ ) เพราะเราต้องการโมเดล บางทีคุณสามารถรับแบบจำลองจากRequestแต่มันจะเป็นกระบวนการที่เกี่ยวข้องมาก

ดังนั้นในทั้งหมดฉันมีเวลายากที่จะแปลงการกระทำ "ง่าย" นี้

ฉันกำลังมองหาปัจจัยการผลิต ฉันผิดที่นี่ทั้งหมดหรือไม่


6
ฟังดูเหมือนคุณจะเข้าใจข้อกังวลที่เกี่ยวข้องแล้วดี มี "กระสุนเงิน" มากมายที่มีตัวอย่างของเล่นที่พิสูจน์ประโยชน์ของพวกเขา แต่หลีกเลี่ยงไม่ได้เมื่อพวกเขาถูกบีบด้วยความเป็นจริงของแอปพลิเคชันที่เกิดขึ้นจริงในชีวิตจริง
Robert Harvey

ตรวจสอบพฤติกรรมของ MediatR โดยพื้นฐานแล้วมันเป็นท่อส่งสินค้าที่ช่วยให้คุณสามารถจัดการกับข้อกังวลต่าง ๆ ได้
fml

คำตอบ:


14

ฉันคิดว่าคุณคาดหวังรูปแบบที่คุณใช้มากเกินไป CQRS ได้รับการออกแบบมาเป็นพิเศษเพื่อจัดการกับความแตกต่างของรูปแบบระหว่างการสืบค้นและคำสั่งไปยังฐานข้อมูลและ MediatR เป็นเพียงคลังข้อความในกระบวนการ CQRS ไม่เรียกร้องให้ขจัดความจำเป็นในการใช้ตรรกะทางธุรกิจอย่างที่คุณคาดหวังไว้ CQRS เป็นรูปแบบสำหรับการเข้าถึงข้อมูล แต่ปัญหาของคุณคือการนำเสนอเลเยอร์ -เส้นทาง, มุมมอง, ตัวควบคุม

ฉันคิดว่าคุณอาจใช้รูปแบบ CQRS ผิดพลาดในการตรวจสอบสิทธิ์ ด้วยการเข้าสู่ระบบมันไม่สามารถเป็นแบบจำลองเป็นคำสั่งใน CQRS เพราะ

คำสั่ง: เปลี่ยนสถานะของระบบ แต่ไม่ส่งคืนค่า
- Martin Fowler CommandQuerySeparation

ในความเห็นของฉันการตรวจสอบความถูกต้องเป็นโดเมนที่ไม่ดีสำหรับ CQRS ด้วยการรับรองความถูกต้องคุณต้องมีการไหลตอบสนองคำขอที่สอดคล้องกันอย่างมากเพื่อให้คุณสามารถ 1. ตรวจสอบข้อมูลรับรองของผู้ใช้ 2. สร้างเซสชันสำหรับผู้ใช้ 3. จัดการกรณีขอบที่หลากหลายที่คุณระบุได้ 4. อนุญาตหรือปฏิเสธผู้ใช้ทันที ในการตอบสนอง

CQRS / MediatR นั้นคุ้มค่าหรือไม่เมื่อพัฒนาแอปพลิเคชัน ASP.NET

CQRS เป็นรูปแบบที่มีการใช้งานเฉพาะอย่างมาก โดยมีวัตถุประสงค์คือสร้างแบบจำลองคิวรีและคำสั่งแทนที่จะมีแบบจำลองสำหรับระเบียนที่ใช้ใน CRUD เมื่อระบบมีความซับซ้อนมากขึ้นความต้องการมุมมองมักจะซับซ้อนมากกว่าเพียงแสดงระเบียนเดียวหรือบันทึกจำนวนหนึ่งและแบบสอบถามสามารถจำลองแบบความต้องการของแอปพลิเคชันได้ดีขึ้น คำสั่งในทำนองเดียวกันสามารถแสดงการเปลี่ยนแปลงหลาย ๆ ระเบียนแทน CRUD ที่คุณเปลี่ยนรายการเดียว Martin Fowler เตือน

เช่นเดียวกับรูปแบบใด ๆ CQRS มีประโยชน์ในบางสถานที่ แต่ไม่ใช่ในรูปแบบอื่น ๆ ระบบจำนวนมากเหมาะสมกับโมเดลจิต CRUD และควรทำในลักษณะนั้น CQRS เป็นก้าวกระโดดทางจิตที่สำคัญสำหรับทุกคนที่เกี่ยวข้องดังนั้นจึงไม่ควรได้รับการแก้ไขหากผลประโยชน์นั้นคุ้มค่ากับการกระโดด ในขณะที่ฉันได้พบกับการใช้ CQRS ที่ประสบความสำเร็จจนถึงขณะนี้กรณีส่วนใหญ่ที่ฉันพบยังไม่ดีเท่าที่ควร แต่ CQRS ถูกมองว่าเป็นกำลังสำคัญในการทำให้ระบบซอฟต์แวร์มีปัญหาร้ายแรง
- Martin Fowler CQRS

ดังนั้นเพื่อตอบคำถามของคุณ CQRS ไม่ควรเป็นทางเลือกแรกในการออกแบบแอปพลิเคชันเมื่อ CRUD เหมาะสม ไม่มีสิ่งใดในคำถามของคุณที่บ่งบอกว่าคุณมีเหตุผลที่จะใช้ CQRS

สำหรับ MediatR เป็นห้องสมุดการส่งข้อความระหว่างดำเนินการโดยมีวัตถุประสงค์เพื่อแยกการร้องขอจากการจัดการคำขอ คุณต้องตัดสินใจอีกครั้งว่าจะปรับปรุงการออกแบบของคุณเพื่อใช้ไลบรารีนี้หรือไม่ ฉันเป็นการส่วนตัวไม่ใช่ผู้สนับสนุนการส่งข้อความระหว่างดำเนินการ การเชื่อมต่อแบบหลวมสามารถทำได้ในวิธีที่ง่ายกว่าการส่งข้อความและฉันขอแนะนำให้คุณเริ่มต้นที่นั่น


1
ฉันเห็นด้วย 100% CQRS นั้นค่อนข้าง hyped ดังนั้นฉันจึงคิดว่า "พวกเขา" เห็นบางสิ่งที่ฉันไม่ได้ เพราะฉันมีเวลายากที่จะเห็นประโยชน์ของ CQRS ในแอพพลิเคชั่นเว็บ CRUD จนถึงตอนนี้สถานการณ์เดียวคือ CQRS + ES ที่เหมาะสมกับฉัน
Snæbjørn

ผู้ชายบางคนในงานใหม่ของฉันตัดสินใจวางระบบ MediatR ในระบบ ASP.Net ใหม่โดยอ้างว่าเป็นสถาปัตยกรรม การใช้งานที่เขาทำนั้นไม่ใช่ DDD, SOLID, DRY และ KISS มันเป็นระบบขนาดเล็กที่เต็มไปด้วย YAGNI และมันเริ่มมานานหลังจากความคิดเห็นบางอย่างเช่นคุณแสดงว่าคุณรวมอยู่ด้วย ฉันพยายามที่จะคิดว่าฉันอาจ refact รหัสเพื่อปรับสถาปัตยกรรมของมันค่อยๆ ฉันมีความคิดเห็นแบบเดียวกันเกี่ยวกับ CQRS นอกชั้นธุรกิจและฉันดีใจที่มีนักออกแบบหลายคนกำลังคิดอย่างนั้น
MFedatto

เป็นเรื่องน่าขันที่จะยืนยันว่าความคิดของการผสมผสาน CQRS / MediatR อาจเกี่ยวข้องกับ YAGNI จำนวนมากและการขาด KISS เมื่อจริง ๆ แล้วทางเลือกที่ได้รับความนิยมเช่นรูปแบบพื้นที่เก็บข้อมูลส่งเสริม YAGNI โดย bloating คลาสพื้นที่เก็บข้อมูลและบังคับให้ อินเตอร์เฟสเพื่อระบุการดำเนินการ CRUD จำนวนมากบนการรวมรูททั้งหมดที่ต้องการใช้อินเทอร์เฟซดังกล่าวมักจะปล่อยให้วิธีการเหล่านั้นไม่ได้ใช้งานหรือเต็มไปด้วยข้อยกเว้น "ไม่ได้ใช้งาน" เนื่องจาก CQRS ไม่ได้ใช้ภาพรวมเหล่านี้จึงสามารถใช้สิ่งที่จำเป็นเท่านั้น
Lesair Valmont

@LesairValmont Repository น่าจะเป็น CRUD เท่านั้น "ระบุการดำเนินการ CRUD จำนวนมาก" ควรเป็น 4 (หรือ 5 ที่มี "รายการ") เท่านั้น หากคุณมีรูปแบบการเข้าถึงแบบสอบถามที่เฉพาะเจาะจงไม่ควรอยู่ในอินเทอร์เฟซที่เก็บข้อมูลของคุณ ฉันไม่เคยเจอปัญหาวิธีการเก็บข้อมูลที่ไม่ได้ใช้ คุณยกตัวอย่างได้ไหม
ซามูเอล

@ ซามูเอล: ฉันคิดว่ารูปแบบที่เก็บนั้นสมบูรณ์แบบสำหรับบางสถานการณ์เช่นเดียวกับ CQRS ที่จริงแล้วในแอปพลิเคชันขนาดใหญ่จะมีบางส่วนที่เหมาะสมที่สุดจะเป็นรูปแบบที่เก็บและส่วนอื่น ๆ ที่จะได้รับประโยชน์มากขึ้นจาก CQRS มันขึ้นอยู่กับปัจจัยต่าง ๆ มากมายเช่นปรัชญาที่ตามมาในส่วนของแอปพลิเคชั่นนั้น (เช่น task-based (CQRS) เทียบกับ CRUD (repo)) ORM ที่ใช้ (ถ้ามี) การสร้างแบบจำลองของโดเมน ( เช่น DDD) สำหรับแคตตาล็อก CRUD อย่างง่าย CQRS นั้นเกินความจริงอย่างแน่นอนและคุณสมบัติการทำงานร่วมกันแบบเรียลไทม์ (เช่นการแชท) จะไม่ใช้
Lesair Valmont

10

CQRS เป็นมากกว่าสิ่งที่การจัดการข้อมูลมากกว่าและไม่ได้มีแนวโน้มที่จะมีเลือดออกมากเกินไปในชั้นแอพลิเคชัน (หรือโดเมนถ้าคุณต้องการเพราะมันมักจะใช้ในระบบ DDD) ตรงกันข้ามแอปพลิเคชัน MVC ของคุณเป็นแอปพลิเคชันเลเยอร์การนำเสนอและควรแยกออกจากแกนการสืบค้น / การคงอยู่ของ CQRS ค่อนข้างดี

สิ่งอื่นที่ควรค่าแก่การสังเกต (จากการเปรียบเทียบLoginวิธีการเริ่มต้นและความต้องการตัวควบคุมแบบบางของคุณ): ฉันจะไม่ปฏิบัติตามค่าเริ่มต้น ASP.NET template / รหัสสำเร็จรูปที่เป็นสิ่งที่เราควรกังวลเกี่ยวกับแนวทางปฏิบัติที่ดีที่สุด

ฉันชอบตัวควบคุมแบบบางเช่นกันเพราะอ่านง่ายมาก คอนโทรลเลอร์แต่ละตัวที่ฉันมีมักจะมีวัตถุ "บริการ" ที่จับคู่กับตัวควบคุมตรรกะที่จำเป็นโดยคอนโทรลเลอร์:

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

ฉันขอแนะนำให้คุณดูการนำเสนอ NDC ของ Jimmy Bogard เกี่ยวกับแนวทางของเขาในการสร้างแบบจำลองคำขอ http https://www.youtube.com/watch?v=SUiWfhAhgQw

จากนั้นคุณจะได้รับความคิดที่ชัดเจนว่า Mediatr ใช้สำหรับอะไร

จิมมี่ไม่มีความสม่ำเสมอในการรับประทานรูปแบบและนามธรรม เขาเน้นในทางปฏิบัติมาก 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 เป็นห้องสมุดที่คุณสามารถใช้เพื่อนำรูปแบบนั้นไปใช้

คุณสามารถใช้ CQRS โดยไม่ต้อง MediatR หรือห้องสมุดการส่งข้อความใด ๆ ในกระบวนการและคุณสามารถใช้ MediatR โดยไม่ต้อง CQRS:

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

ในทางเทคนิคในคำสั่งคำอธิบายดั้งเดิมไม่ควรคืนค่าใด ๆ (เป็นโมฆะ) ที่ฉันพบว่าโง่เพราะไม่มีวิธีที่ง่ายในการรับ id ที่สร้างขึ้นจากวัตถุที่สร้างขึ้นใหม่: /programming/4361889/how-to- ได้รับรหัสในการสร้างเมื่อ-ใช้-cqrs

ดังนั้นคุณต้องสร้างรหัสในแต่ละครั้งด้วยตัวคุณเองแทนที่จะปล่อยให้ฐานข้อมูลทำ


หากคุณต้องการเรียนรู้เพิ่มเติม: https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf


1
ฉันขอท้าให้คุณยืนยันว่าคำสั่งของ CQRS สำหรับการเก็บข้อมูลใหม่ในฐานข้อมูลซึ่งไม่สามารถส่งคืน Id ที่สร้างจากฐานข้อมูลใหม่คือ "stupid" ฉันคิดว่านี่เป็นเรื่องทางปรัชญา โปรดจำไว้ว่า DDD และ CQRS ส่วนใหญ่เกี่ยวกับการเปลี่ยนแปลงข้อมูล เมื่อคุณคิดเกี่ยวกับมันสองครั้งคุณเริ่มตระหนักว่าการกระทำของข้อมูลที่ยังคงมีอยู่เป็นการดำเนินการกลายพันธุ์ของข้อมูล และไม่เพียง แต่เกี่ยวกับ ID ใหม่ แต่ยังอาจเป็นฟิลด์ที่เต็มไปด้วยข้อมูลเริ่มต้นทริกเกอร์และ procs ที่เก็บไว้ซึ่งอาจเปลี่ยนแปลงข้อมูลของคุณได้เช่นกัน
Lesair Valmont

แน่ใจว่าคุณสามารถส่งเหตุการณ์บางอย่างเช่น "ItemCreated" โดยมีรายการใหม่เป็นอาร์กิวเมนต์ หากคุณกำลังติดต่อกับโปรโตคอลตอบกลับคำขอและใช้ CQRS "ของจริง" ต้องทราบรหัสล่วงหน้าเพื่อให้คุณสามารถส่งผ่านไปยังฟังก์ชันการสืบค้นแยกต่างหาก - ไม่มีอะไรผิดปกติ ในหลายกรณี CQRS นั้นเกินความจำเป็น คุณสามารถอยู่ได้โดยปราศจากมัน มันไม่มีอะไรนอกจากวิธีการจัดโครงสร้างโค้ดของคุณและขึ้นอยู่กับว่าคุณใช้โปรโตคอลใดเป็นส่วนใหญ่
Konrad

และคุณสามารถบรรลุความไม่เปลี่ยนแปลงของข้อมูลโดยไม่ต้อง CQRS
Konrad
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.