Ao seguir o SRP, como devo lidar com validar e salvar entidades?


10

Ultimamente, tenho lido o Clean Code e vários artigos on-line sobre o SOLID. Quanto mais leio sobre isso, mais sinto que não sei de nada.

Digamos que estou criando um aplicativo da Web usando o ASP.NET MVC 3. Digamos que eu tenha um UsersControllercom uma Createação como esta:

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

    }
}

Nesse método de ação, quero salvar um usuário no banco de dados se os dados inseridos forem válidos.

Agora, de acordo com o Princípio da Responsabilidade Única, um objeto deve ter uma única responsabilidade e essa responsabilidade deve ser totalmente encapsulada pela classe. Todos os seus serviços devem estar estreitamente alinhados com essa responsabilidade. Como validação e salvamento no banco de dados são duas responsabilidades separadas, acho que devo criar uma classe separada para lidar com elas dessa maneira:

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);
        }
    }
}

Isso faz algum sentido para mim, mas não tenho certeza de que esse seja o caminho certo para lidar com coisas assim. Por exemplo, é perfeitamente possível passar uma instância inválida CreateUserViewModelpara a IUserServiceclasse. Eu sei que poderia usar o DataAnnotations incorporado, mas e quando eles não forem suficientes? Imagem que meu ICreateUserValidatorverifica o banco de dados para ver se já existe outro usuário com o mesmo nome ...

Outra opção é deixar que IUserServicea validação tome conta da seguinte maneira:

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;
    }
}

Mas sinto que estou violando o Princípio da Responsabilidade Única aqui.

Como devo lidar com algo assim?


A userclasse não deve lidar com a validação? SRP ou não, não vejo por que a userinstância não deve saber quando é válida ou não e confiar em algo mais para determinar isso. Que outras responsabilidades a classe tem? Além disso, quando as useralterações a validação provavelmente mudar, a terceirização que para uma classe diferente criará apenas uma classe fortemente acoplada.
sebastiangeiger

Respostas:


4

Realmente não acho que você esteja violando o princípio de responsabilidade única no seu segundo exemplo.

  • A UserServiceclasse tem apenas um motivo para mudar: se houver necessidade de alterar a maneira como você salva um usuário.

  • A ICreateUserValidatorclasse tem apenas um motivo para alterar: se houver necessidade de alterar a maneira como você valida um usuário.

Devo admitir que sua primeira implementação é mais intuitiva. No entanto, a validação deve ser feita pela entidade que cria o usuário. O próprio criador não deve ser responsável pela validação; em vez disso, deve delegar a responsabilidade para uma classe de validador (como em sua segunda implementação). Portanto, não acho que o segundo design não tenha SRP.


11
Padrão de responsabilidade única? Princípio, talvez?
yannis

Sim, claro :) Corrigido!
Guven

0

Parece-me que o primeiro exemplo é "mais próximo" do SRP verdadeiro; é responsabilidade do controlador no seu caso saber como conectar as coisas e como transmitir o ViewModel.

Não faria mais sentido que IsValid / ValidationMessages inteiro fizesse parte do próprio ViewModel? Eu não brinquei com o MVVM, mas parece que o antigo padrão Model-View-Presenter, onde era aceitável o Presenter lidar com coisas assim porque estava diretamente relacionado à apresentação. Se o seu ViewModel puder verificar a validade, não há chance de passar um inválido para o método Create do UserService.


Eu sempre pensei que os ViewModels deveriam ser simples DTOs sem muita lógica neles. Eu não tenho certeza se eu deveria colocar algo como verificar o banco de dados em um ViewModel ...
Kristof Claes

Eu acho que isso dependeria do que sua validação implica; se o ViewModel apenas expuser o IsValidbooleano e o ValidationMessagesarray, ele ainda poderá chamar uma classe Validator e não precisar se preocupar com o modo como a validação está sendo implementada. O controlador pode verificar se primeiro, por exemplo, if (userViewModel.IsValid) { userService.Create(userViewModel); }basicamente o ViewModel deve saber se é válido, mas não como ele é válido.
Wayne Molina
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.