Quando si segue SRP, come devo gestire la convalida e il salvataggio delle entità?

10

Ho letto Pulisci codice e vari articoli online su SOLID ultimamente, e più ne leggo, più mi sento come se non sapessi nulla.

Diciamo che sto costruendo un'applicazione web usando ASP.NET MVC 3. Diciamo che ho un UsersController con un'azione Create come questa:

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

    }
}

In quel metodo di azione voglio salvare un utente nel database se i dati inseriti sono validi.

Ora, secondo il Principio di Responsabilità Unica, un oggetto dovrebbe avere una singola responsabilità e tale responsabilità dovrebbe essere interamente incapsulata dalla classe. Tutti i suoi servizi dovrebbero essere strettamente allineati a tale responsabilità. Poiché la convalida e il salvataggio nel database sono due responsabilità separate, suppongo che dovrei creare una classe separata per gestirli in questo modo:

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

Questo mi rende alcuni sensato, ma non sono affatto sicuro che questo sia il modo giusto per gestire cose come questa. Ad esempio, è completamente possibile passare un'istanza non valida di CreateUserViewModel alla classe IUserService . So che potrei usare le NoteAnnotations integrate, ma cosa succede quando non sono sufficienti? Immagine che il mio ICreateUserValidator controlla il database per vedere se esiste già un altro utente con lo stesso nome ...

Un'altra opzione è lasciare che IUserService si prenda cura della convalida in questo modo:

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

Ma sento che sto violando il Principio di Responsabilità Unica qui.

Come devo comportarmi con qualcosa del genere?

    
posta Kristof Claes 07.10.2011 - 15:52
fonte

2 risposte

4

Non penso davvero che tu stia violando il Principio della singola responsabilità nel tuo secondo esempio.

  • La classe UserService ha solo un motivo per cambiare: se è necessario cambiare il modo in cui si salva un utente.

  • La classe ICreateUserValidator ha solo un motivo per cambiare: se è necessario cambiare il modo in cui convalidare un utente.

Devo ammettere che la tua prima implementazione è più intuitiva. Tuttavia, la convalida deve essere eseguita dall'entità che crea l'utente. Lo stesso creatore non dovrebbe essere responsabile per la convalida; dovrebbe piuttosto delegare la responsabilità a una classe di validatori (come nella tua seconda implementazione). Quindi, non penso che il secondo progetto manchi di SRP.

    
risposta data 12.11.2011 - 00:05
fonte
0

Mi sembra che il primo esempio sia "più vicino" al vero SRP; è responsabilità del Controller nel tuo caso sapere come collegare le cose e come passare lungo ViewModel.

Non avrebbe più senso avere l'intero IsValid / ValidationMessages come parte del ViewModel stesso? Non mi sono dilettato con MVVM, ma sembra il vecchio pattern Model-View-Presenter, in cui era normale che il Presenter gestisse cose del genere perché era direttamente correlato alla presentazione. Se il tuo ViewModel può verificare se stesso per la validità, non c'è possibilità di passare uno valido al metodo Create di UserService.

    
risposta data 07.10.2011 - 19:49
fonte

Leggi altre domande sui tag