Saat mengikuti SRP, bagaimana saya harus berurusan dengan memvalidasi dan menyimpan entitas?

10

Saya telah membaca Kode Bersih dan berbagai artikel online tentang SOLID belakangan ini, dan semakin saya membaca tentangnya, semakin saya merasa tidak tahu apa-apa.

Katakanlah saya sedang membangun sebuah aplikasi web menggunakan katakanlah ASP.NET MVC 3. Mari saya memiliki UsersControllerdengan Createtindakan seperti ini:

public class UsersController : Controller
{
    public ActionResult Create(CreateUserViewModel viewModel)
    {

    }
}

Dalam metode tindakan itu saya ingin menyimpan pengguna ke database jika data yang dimasukkan valid.

Sekarang, menurut Prinsip Tanggung Jawab Tunggal, sebuah objek harus memiliki tanggung jawab tunggal, dan tanggung jawab itu harus sepenuhnya dienkapsulasi oleh kelas. Semua layanannya harus selaras dengan tanggung jawab itu. Karena validasi dan penyimpanan ke database adalah dua tanggung jawab terpisah, saya kira saya harus membuat kelas terpisah untuk menangani mereka seperti ini:

public class UsersController : Controller
{
    private ICreateUserValidator validator;
    private IUserService service;

    public UsersController(ICreateUserValidator validator, IUserService service)
    {
        this.validator = validator;
        this.service= service;
    }

    public ActionResult Create(CreateUserViewModel viewModel)
    {
        ValidationResult result = validator.IsValid(viewModel);

        if (result.IsValid)
        {
            service.CreateUser(viewModel);
            return RedirectToAction("Index");
        }
        else
        {
            foreach (var errorMessage in result.ErrorMessages)
            {
                ModelState.AddModelError(String.Empty, errorMessage);
            }
            return View(viewModel);
        }
    }
}

Yang membuat beberapa akal bagi saya, tapi saya sama sekali tidak yakin bahwa ini adalah cara yang tepat untuk menangani hal-hal seperti ini. Hal ini misalnya sepenuhnya mungkin untuk melewati sebuah instance valid dari CreateUserViewModelke IUserServicekelas. Saya tahu saya bisa menggunakan DataAnnotations bawaan, tetapi bagaimana ketika itu tidak cukup? Gambar yang saya ICreateUserValidatorperiksa database untuk melihat apakah sudah ada pengguna lain dengan nama yang sama ...

Pilihan lain adalah membiarkan IUserServicevalidasi seperti ini:

public class UserService : IUserService
{
    private ICreateUserValidator validator;

    public UserService(ICreateUserValidator validator)
    {
        this.validator = validator;
    }

    public ValidationResult CreateUser(CreateUserViewModel viewModel)
    {
        var result = validator.IsValid(viewModel);

        if (result.IsValid)
        {
            // Save the user
        }

        return result;
    }
}

Tetapi saya merasa saya melanggar Prinsip Tanggung Jawab Tunggal di sini.

Bagaimana saya harus berurusan dengan hal seperti ini?

Kristof Claes
sumber
Bukankah seharusnya userkelas menangani validasi? SRP atau tidak, saya tidak melihat mengapa userinstance tidak harus tahu kapan valid atau tidak dan bergantung pada sesuatu yang lain untuk menentukan itu. Apa tanggung jawab lain yang dimiliki kelas? Ditambah ketika userperubahan validasi mungkin akan berubah, jadi outsourcing yang ke kelas yang berbeda hanya akan membuat kelas yang digabungkan secara ketat.
sebastiangeiger

Jawaban:

4

Saya benar-benar tidak berpikir Anda melanggar Prinsip Tanggung Jawab Tunggal dalam contoh kedua Anda.

  • The UserServicekelas hanya memiliki satu alasan untuk perubahan: jika ada kebutuhan untuk mengubah cara Anda menyimpan pengguna.

  • The ICreateUserValidatorkelas hanya memiliki satu alasan untuk mengubah: jika ada kebutuhan untuk mengubah cara Anda memvalidasi pengguna.

Saya harus mengakui bahwa implementasi pertama Anda lebih intuitif. Namun, validasi harus dilakukan oleh entitas yang menciptakan pengguna. Pembuatnya sendiri seharusnya tidak bertanggung jawab atas validasi; lebih baik mendelegasikan tanggung jawab ke kelas validator (seperti dalam implementasi kedua Anda). Jadi, saya tidak berpikir desain kedua tidak memiliki SRP.

Guven
sumber
1
Pola Tanggung Jawab Tunggal? Prinsipnya, mungkin?
yannis
Ya, tentu saja :) Sudah diperbaiki!
Guven
0

Tampak bagi saya bahwa contoh pertama adalah "lebih dekat" ke SRP sejati; itu adalah tanggung jawab Pengendali dalam kasus Anda untuk mengetahui cara menghubungkan semuanya dan bagaimana cara meneruskan ViewModel.

Bukankah lebih masuk akal jika seluruh IsValid / ValidationMessages menjadi bagian dari ViewModel itu sendiri? Saya belum mencoba-coba MVVM, tetapi sepertinya pola Model-View-Presenter yang lama, di mana tidak masalah bagi Presenter untuk menangani hal-hal seperti itu karena itu berhubungan langsung dengan presentasi. Jika ViewModel Anda dapat memeriksa sendiri validitasnya, tidak ada peluang untuk memberikan yang tidak valid ke metode Buat UserService.

Wayne Molina
sumber
Saya selalu berpikir ViewModels harus menjadi DTO sederhana tanpa terlalu banyak logika di dalamnya. Saya tidak yakin apakah saya harus memasukkan sesuatu seperti memeriksa basis data dalam ViewModel ...
Kristof Claes
Saya kira itu akan tergantung pada apa yang diperlukan validasi Anda; jika ViewModel hanya mengekspos IsValidboolean dan ValidationMessagesarray, itu masih bisa memanggil ke kelas Validator dan tidak perlu khawatir tentang bagaimana validasi sedang dilaksanakan. Pengontrol dapat memeriksa terlebih dahulu misalnya if (userViewModel.IsValid) { userService.Create(userViewModel); }Pada dasarnya ViewModel harus tahu apakah itu valid, tetapi tidak bagaimana ia tahu itu valid.
Wayne Molina