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:

image

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:

  1. 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.
  2. 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.

Victor Cavalcante

Victor Cavalcante é marido da Mariana Frioli, pai do Eduardo, Microsoft MVP, trabalha com desenvolvimento web a 11 anos e é focado em padrões de desenvolvimento para web. Trabalha na Lambda3 (www.lambda3.com.br), empresa ágil que insiste em fazer software certo. É ainda professor universitário e atua no grupo .NET Architects desde seu início. Acompanhe seu twitter para saber as novidades:@vcavalcante.