Como nos 2 últimos posts eu falei sobre dúvidas que eu tinha recebido, vou continuar na mesma linha, só que agora busquei a dúvida nesta thread do grupo .NetArchitects, entre outras (muitas) coisas que foram discutidas foi levantado a dúvida sobre a responsabilidade da View, na dúvida o Emmanuel propõe um cenário que está exposto abaixo:
Se você tem que exibir transações de conta corrente, por exemplo, débito e crédito, mas quando o saldo fica negativo você quer colocar a linha em vermelho, é uma regra de apresentação. Mas onde estaria esse código? Acho que não estaria no domínio por que não diz respeito a ele... Seria no Controller?
Esta é uma pergunta mais comum do que se imagina, já me perguntaram diversas vezes sobre a responsabilidade de cada letra do MVC, e é por isso que eu coloquei como título deste post como “Cada um no seu quadrado”.
A resposta simples e direta para a pergunta acima é que essa regra fica na View, mas vamos elaborar um pouco mais, por que não no Controller? Simples, por que assim ele estaria invadindo o quadrado da View, vou exemplificar o meu ponto de vista:
No cenário proposto pelo Emmanuel, o Controller enviaria os dados através da ViewData para a View e ela por sua vez verificaria se o Saldo é negativo, caso fosse exibiria ele em vermelho, vamos ver um pouco de código.
Vamos começar pelo modelo, para representar o cenário criei 3 classes, a Conta que contem os dados da conta e as transações, a Transação que representa cada transação da conta e o Repositório que para o exemplo irá preencher os objetos com dados randômicos.
Conta
public class Conta
{
public int Numero { get; set; }
public int Agencia { get; set; }
public IList<Transacao> Transacoes { get; set; }
public double Saldo { get; set; }
}
Transação
public class Transacao
{
public DateTime Data { get; set; }
public string Descricao { get; set; }
public double Valor { get; set; }
}
Repositório
public class ContaRepositorio
{
public Conta get(int Agencia, int Numero) {
//aqui faria a consulta no banco, vou fazer um
//faz de conta para facilitar
Conta conta = new Conta();
conta.Agencia = Agencia;
conta.Numero = Numero;
conta.Transacoes = new List<Transacao>();
Random rnd = new Random();
for (int i = 0; i < 15; i++)
{
conta.Transacoes.Add(new Transacao{Data = DateTime.Now.AddDays(-i),Descricao = "Transação nº "+i, Valor= rnd.Next(0,358)});
}
conta.Saldo = rnd.Next(-1245, 1245);
return conta;
}
}
Vamos ver agora o Controller, criei uma única action que mandará através da ViewData os dados da Conta, neste ponto é que surge a dúvida, posso colocar a inteligência de trocar a cor aqui? Bom, tecnicamente pode, poderia criar um objeto intermediário que tem sido chamado de ViewModel para armazenar esta “condição”, mas na minha opinião o Controller estará invadindo o quadrado da View. Na soluçao proposta abaixo eu passo somente os dados para a View.
Controller
public class ContaController : Controller
{
//
// GET: /Conta/
public ActionResult Index()
{
ContaRepositorio rep = new ContaRepositorio();
ViewData.Model = rep.get(1234, 123456);
return View();
}
}
Vamos agora para a View, de acordo com o valor do saldo é escolhido a classe que será aplicano na tag que contém o saldo.
View
<h2>Extato de Conta</h2>
<fieldset>
<p>Numero: <%= Html.Encode(Model.Numero) %></p>
<p>Agencia: <%= Html.Encode(Model.Agencia) %></p>
<table>
<thead>
<tr>
<th>Data</th>
<th>Descrição</th>
<th>Valor</th>
</tr>
</thead>
<tbody>
<%foreach (var item in Model.Transacoes){%>
<tr>
<td><%= Html.Encode(String.Format("{0:d/M/yyyy}", item.Data))%></td>
<td><%= Html.Encode( item.Descricao)%></td>
<td><%= Html.Encode(String.Format("{0:F}", item.Valor)) %></td>
</tr>
<%} %>
</tbody>
</table>
<%
string classe;
if (Model.Saldo < 0)
{
classe = "negativo";
}
else {
classe = "positivo";
} %>
<p>Saldo: <span class="<%=classe%>"><%= Html.Encode(String.Format("{0:F}", Model.Saldo)) %></span></p>
</fieldset>
No CSS adicionei as seguintes classes:
.positivo{color:Blue;}
.negativo{color:Red;}
O resultado esta na imagem abaixo:
Este código eu fiz com a intenção de demonstrar que a View não é burra (no bom sentido), ela pode e deve conter regras que são de interface, como no exemplo acima.
O Emmanuel quis complicar um pouco mais o cenário e colocou o seguinte:
E se esse núcleo do meu sistema é consumido por diversas interfaces? Mobile, desktop, web; essa regra da interface, não deveria estar em outro local que não a interface?
Quando tive os primeiros contatos com o ASP.NET MVC tive a mesma dúvida, mas com o tempo e várias pesquisas fiquei convencido que isto não é possível e nem interessante, digo isso por 2 motivos:
- O primeiro é técnico, como o MVC é um padrão de Interface Gráfica ele depende muito do ambiente, que neste caso é o ASP.NET MVC, ou seja, eu não conseguiria reutilizar o Controller do ASP.NET MVC para uma aplicação Windows Form ou uma aplicação Mobile.
- O segundo é sobre os diferente tipos de mídias que poderíamos utilizar para a nossa interface, cada uma dessas mídias tem suas próprias preocupações de acessibilidade e usabilidade e para lidar com essas preocupações cada uma delas deve ter autonomia.
O código deste exemplo já está disponível.
Bom, espero ter respondido a dúvida do Emmanuel e de outras pessoas. Até a próxima.