Wie soll ich mit der Validierung und Speicherung von Entitäten umgehen, wenn ich SRP folge?

10

Ich habe in letzter Zeit Clean Code und verschiedene Online-Artikel über SOLID gelesen. Je mehr ich darüber lese, desto mehr habe ich das Gefühl, nichts zu wissen.

Angenommen, ich erstelle eine Webanwendung mit ASP.NET MVC 3. Angenommen, ich habe eine UsersControllermit einer CreateAktion wie dieser:

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

    }
}

Bei dieser Aktionsmethode möchte ich einen Benutzer in der Datenbank speichern, wenn die eingegebenen Daten gültig sind.

Nach dem Prinzip der Einzelverantwortung sollte ein Objekt nun eine Einzelverantwortung haben, und diese Verantwortung sollte vollständig von der Klasse eingekapselt werden. Alle seine Dienstleistungen sollten eng an dieser Verantwortung ausgerichtet sein. Da Validierung und Speichern in der Datenbank zwei separate Verantwortlichkeiten sind, sollte ich eine separate Klasse erstellen, um sie wie folgt zu behandeln:

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

Das macht etwas Sinn für mich, aber ich bin nicht sicher , dass dies der richtige Weg , um Griff Dinge wie diese. Es ist beispielsweise durchaus möglich, eine ungültige Instanz von CreateUserViewModelan die IUserServiceKlasse zu übergeben. Ich weiß, dass ich die integrierten DataAnnotations verwenden könnte, aber was ist, wenn sie nicht ausreichen? Bild, das ich in ICreateUserValidatorder Datenbank überprüfe, um festzustellen, ob es bereits einen anderen Benutzer mit demselben Namen gibt ...

Eine andere Möglichkeit besteht darin, IUserServicedie Validierung wie folgt durchführen zu lassen :

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

Aber ich habe das Gefühl, dass ich hier gegen das Prinzip der Einzelverantwortung verstoße.

Wie soll ich mit so etwas umgehen?

Kristof Claes
quelle
Sollte die userKlasse die Validierung nicht durchführen? SRP oder nicht, ich verstehe nicht, warum die userInstanz nicht wissen sollte, wann sie gültig ist oder nicht, und verlasse mich auf etwas anderes, um dies zu bestimmen. Welche anderen Aufgaben hat die Klasse? Wenn sich userÄnderungen ändern, ändert sich wahrscheinlich die Validierung. Wenn Sie diese also an eine andere Klasse auslagern, wird nur eine eng gekoppelte Klasse erstellt.
Sebastiangeiger

Antworten:

4

Ich glaube wirklich nicht, dass Sie in Ihrem zweiten Beispiel gegen das Prinzip der Einzelverantwortung verstoßen.

  • Die UserServiceKlasse hat nur einen Grund, sich zu ändern: Wenn die Art und Weise, wie Sie einen Benutzer speichern, geändert werden muss.

  • Die ICreateUserValidatorKlasse hat nur einen Grund zur Änderung: Wenn die Art und Weise, wie Sie einen Benutzer validieren, geändert werden muss.

Ich muss zugeben, dass Ihre erste Implementierung intuitiver ist. Die Validierung sollte jedoch von der Entität durchgeführt werden, die den Benutzer erstellt. Der Ersteller selbst sollte nicht für die Validierung verantwortlich sein. Es sollte die Verantwortung eher an eine Validator-Klasse delegieren (wie in Ihrer zweiten Implementierung). Ich glaube also nicht, dass dem zweiten Design SRP fehlt.

Guven
quelle
1
Einzelverantwortungsmuster? Prinzip vielleicht?
Yannis
Ja natürlich :) Korrigiert!
Guven
0

Es scheint mir, dass das erste Beispiel der wahren SRP "näher" ist; In Ihrem Fall liegt es in der Verantwortung des Controllers, zu wissen, wie die Dinge verkabelt und wie das ViewModel weitergegeben wird.

Wäre es nicht sinnvoller, wenn die gesamten IsValid / ValidationMessages Teil des ViewModel selbst wären? Ich habe mich nicht mit MVVM beschäftigt, aber es scheint wie das alte Model-View-Presenter-Muster, bei dem es für den Presenter in Ordnung war, solche Dinge zu handhaben, da es in direktem Zusammenhang mit der Präsentation stand. Wenn Ihr ViewModel sich selbst auf Gültigkeit prüfen kann, besteht keine Möglichkeit, ein ungültiges an die Create-Methode des UserService zu übergeben.

Wayne Molina
quelle
Ich war immer der Meinung, dass ViewModels einfache DTOs ohne zu viel Logik sein sollten. Ich bin mir nicht sicher, ob ich so etwas wie das Überprüfen der Datenbank in ein ViewModel einfügen soll ...
Kristof Claes
Ich denke, es würde davon abhängen, was Ihre Validierung beinhaltet. Wenn das ViewModel nur den IsValidBooleschen Wert und das ValidationMessagesArray verfügbar macht , kann es dennoch eine Validator-Klasse aufrufen und muss sich keine Gedanken darüber machen, wie die Validierung implementiert wird. Der Controller könnte dies zuerst überprüfen, z. B. sollte if (userViewModel.IsValid) { userService.Create(userViewModel); }das ViewModel grundsätzlich wissen, ob es gültig ist, aber nicht, woher es weiß, dass es gültig ist.
Wayne Molina