Saya telah mencari CQRS / MediatR belakangan ini. Tetapi semakin saya menelusuri semakin kurang saya menyukainya. Mungkin saya salah paham tentang sesuatu / segalanya.
Jadi itu mulai luar biasa dengan mengklaim mengurangi controller Anda untuk ini
public async Task<ActionResult> Edit(Edit.Query query)
{
var model = await _mediator.SendAsync(query);
return View(model);
}
Yang sangat cocok dengan pedoman pengontrol yang tipis. Namun itu meninggalkan beberapa detail yang cukup penting - penanganan kesalahan.
Mari kita lihat Login
aksi default dari proyek MVC baru
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);
}
Konversi yang memberi kita banyak masalah dunia nyata. Ingat tujuannya adalah untuk menguranginya menjadi
public async Task<IActionResult> Login(Login.Command command, string returnUrl = null)
{
var model = await _mediator.SendAsync(command);
return View(model);
}
Salah satu solusi yang mungkin untuk ini adalah mengembalikan CommandResult<T>
bukan model
dan kemudian menangani CommandResult
dalam filter tindakan pos. Seperti yang dibahas di sini .
Salah satu implementasi CommandResult
bisa seperti ini
public interface ICommandResult
{
bool IsSuccess { get; }
bool IsFailure { get; }
object Result { get; set; }
}
Namun itu tidak benar-benar menyelesaikan masalah kita dalam Login
tindakan, karena ada beberapa kondisi kegagalan. Kita dapat menambahkan status kegagalan tambahan ini ICommandResult
tetapi itu merupakan awal yang bagus untuk kelas / antarmuka yang sangat besar. Orang mungkin mengatakan itu tidak sesuai dengan Tanggung Jawab Tunggal (SRP).
Masalah lainnya adalah returnUrl
. Kami memiliki return RedirectToLocal(returnUrl);
kode ini. Entah bagaimana kita perlu menangani argumen bersyarat berdasarkan status keberhasilan perintah. Sementara saya pikir itu bisa dilakukan (saya tidak yakin apakah ModelBinder dapat memetakan argumen FromBody dan FromQuery ( returnUrl
adalah FromQuery) ke model tunggal). Orang hanya bisa bertanya-tanya skenario gila macam apa yang bisa terjadi.
Validasi model juga menjadi lebih kompleks seiring dengan pengembalian pesan kesalahan. Ambil ini sebagai contoh
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
Kami melampirkan pesan kesalahan bersama dengan model. Hal semacam ini tidak dapat dilakukan dengan menggunakan Exception
strategi (seperti yang disarankan di sini ) karena kita memerlukan model. Mungkin Anda bisa mendapatkan modelnya dari Request
tetapi itu akan menjadi proses yang sangat terlibat.
Jadi secara keseluruhan saya mengalami kesulitan mengubah tindakan "sederhana" ini.
Saya mencari input. Apakah saya benar-benar salah di sini?