The @utluiz response is correct within a 3 layer context (presentation, service, and data), but as we are talking about MVC, validation should be done essentially in Model
, but not only in it.
Why?
The essential function of Model
is to model how information circulates within the system from the point of view of each entity. The essential function of Controller
is to harmonize and coordinate the information flows - coming from the presentation, the data layer or some external service.
Therefore, validation functions for any given data are usually generated at Model
level. The Controller
only interprets this result of the validations. This is done because validation is an aspect at the data level. Elegantly, this is done in ASP.NET MVC using Attributes
.
Meanwhile, relationships between entities ( Models
) are resolved at Controller
level (comparisons, insertions, deletions, sorts, etc.).
Examples
Consider Model Pessoa
below:
public class Pessoa
{
[Key]
public Guid PessoaId {get;set;}
[Display(Name = "Nome")]
[Required(ErrorMessage = "O preenchimento do nome da pessoa é obrigatório.")]
public String Nome {get;set;}
[Required(ErrorMessage = "O preenchimento do CPF da pessoa é obrigatório.")]
[CPF]
public Decimal Cpf {get;set;}
public virtual ICollection<Produto> Produtos {get;set;}
}
When saving a Pessoa
to a database, I do not need to write a validation row in my Controller
because all aspects of data are being treated within Model
succinctly: the CPF has an attribute that validates the CPF, the name can not be filled empty and the primary key must be unique ( [Key]
).
The Controller
does nothing here, but check through another class if the Model
is valid:
[HttpPost]
public ActionResult Create(Pessoa pessoa)
{
// Se o Model for válido
if (ModelState.IsValid)
{
// Gera a chave, salva e retorna
pessoa.PessoaId = Guid.NewGuid();
context.Pessoas.Add(pessoa);
context.SaveChanges();
return RedirectToAction("Index");
}
// Se não for válido, simplesmente retorna o objeto Pessoa de novo.
// O resultado das validações está dentro da classe ModelState.
return View(pessoa);
}
Now suppose I want to give a discount on a Produto
purchased by the user. This is dealt with in Controller
because we are dealing with a Pessoa
and Produtos
acquired by it:
public class PessoasController : Controller
{
...
public ActionResult ConcederDescontoUltimoProduto(Guid? id)
{
var pessoa = context.Pessoas.Include(p => p.Produtos).SingleOrDefault(p => p.PessoaId == id);
if (pessoa.Produtos.Count > 5)
{
var produto = pessoa.Produtos.Last();
// Vou conceder 10% de desconto no valor do produto
produto.Preco -= produto.Preco * 0.1;
context.Entry(produto).State = EntityState.Modified;
context.SaveChanges();
}
return RedirectToAction("Index");
}
}
It is also possible to leave this negotiating aspect in charge of other specific classes, but I consider a long and unnecessary effort, in which nothing contributes to the security and class responsibility of the system.