Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clean up and validating ViewModel with MVC, how?

I have some questions about how is the best way to implement DDD principles with best pratices of asp.net mvc. Actually, I would like to know, how have you been doing the validation (in viewmodel or model)?

I have this DomainModel and ViewModel:

public class Product {
   public int Id { get; protected set; }
   public string Name { get; set; }
   public decimal Price { get; set; }
   public int Stock { get; set; }
   public Category Category { get; set; }
   public Supplier Supplier { get; set; }
   public string Code { get; set; } // it has to be unique
}

public class ProductViewModel {
   public int Id { get; set; }
   /* other simple properties */
   public int IdCategory { get; set; }
   public int IdSupplier { get; set; }
   public string Code { get; set; } 
}

Ok. The model is mapped with NHibernate and works fine. I want to know, if it's better create a validation for ViewModel or the DomainModel? I mean, when I receve the ViewModel on a action of asp.net mvc, I will validate it, but if I add business rules on viewmodel, won't I doing wrong? I ask this because I know it's better to add these business validation to my domain, but should I do two validations on my post before persist? Look my action on asp.net mvc:

[Post]
public ActionResult Update(ProductViewModel viewModel) {

  // what kind of validations should I do here? 
  if (invalid) {
    return View(viewModel);
  } 

  // how can I return the errors to my View?

  // Is here any best pratice to transform from ViewModel to Domain instance?
  Product product = ???

  _repository.Save(product);

  return RedirectToAction("Index");
}

Could someone do an example by code?

like image 570
Felipe Oriani Avatar asked Jan 16 '23 17:01

Felipe Oriani


1 Answers

I would say that you should perform validations at the two levels. In your view model you will do surface validations such as for example this field is required and this field must be of the following format whereas in your business layer you should be validating business rules such as the username already exists, ...

So let's take an example of how a typical POST action might look like:

[HttpPost]
public ActionResult Update(ProductViewModel viewModel) 
{
    if (!ModelState.IsValid) 
    {
        // The view model is invalid => redisplay the view in order
        // to show the error messages
        return View(viewModel);
    }    

    // at this stage we know that the view model is valid =>
    // now we can map it to a domain model that we want to update:

    Product product = Repository.GetModel(viewModel.Id);

    // I use AutoMapper (http://automapper.org/) to map between 
    // my domain models and my view models. In this example
    // we are updating only the properties of the domain model
    // that were part of the view model and that the user is allowed
    // to modify in this view => we are merging
    Mapper.Map<ProductViewModel, Product>(viewModel, product);

    // now we can process the domain model
    Repository.Update(product);

    return RedirectToAction("Index");
}

and if you wanted to handle domain model errors there are different possibilities. Personally I like the TryXXX pattern:

string errorMessage;
if (!Repository.TryUpdate(product, out errorMessage))
{
    // the business validation failed => add the error message to the modelstate
    // and redisplay the view
    ModelState.AddModelError("", errorMessage);
    return View(viewModel);
}

Another possibility is to pass the ModelState dictionary to the business layer and let it add the model state error directly. This way you can once again simply test if (!ModelState.IsValid) in the view to know whether something went wrong in the business layer and redisplay the same view to show the error message.

As far as validation in the view model is concerned there are different ways. You could use the Microsoft's official way which is by decorating your view model properties with validation attributes. For example:

public class ProductViewModel 
{
    [Required]
    public string Foo { get; set; }

    ...
}

Personally I don't use those attributes. I find them extremely limiting especially when you want to do a little more complex validations with dependent properties and so on. For this reason I use FluentValidation.NET which integrates very nicely with ASP.NET MVC and allows me to easily unit test my validation rules.

like image 85
Darin Dimitrov Avatar answered Jan 30 '23 22:01

Darin Dimitrov