Editar: atualizei esta resposta no meu blog:
http://www.samwheat.com/post/The-function-of-ViewModels-in-MVC-web-development
Minha resposta é um pouco longa, mas acho importante comparar modelos de exibição com outros tipos de modelos comumente usados para entender por que eles são diferentes e por que são necessários.
Para resumir e responder diretamente à pergunta que é feita:
De um modo geral, um modelo de vista é um objeto que contém todas as propriedades e métodos necessários para renderizar uma vista. As propriedades do modelo de exibição geralmente estão relacionadas a objetos de dados, como clientes e pedidos, e também contêm propriedades relacionadas à página ou ao próprio aplicativo, como nome de usuário, nome do aplicativo etc. crie uma página html. Uma das muitas razões para usar um modelo de exibição é que os modelos de exibição fornecem uma maneira de testar unidades determinadas tarefas de apresentação, como lidar com a entrada do usuário, validar dados, recuperar dados para exibição etc.
Aqui está uma comparação dos modelos de entidade (modelos a.ka. do DTO), modelos de apresentação e modelos de exibição.
Objetos de transferência de dados, também conhecidos como "Modelo"
Um Data Transfer Object (DTO) é uma classe com propriedades que correspondem a um esquema de tabela em um banco de dados. Os DTOs são nomeados por seu uso comum na transferência de dados de e para um repositório de dados.
Características dos DTOs:
• São objetos de negócios - sua definição depende dos dados do aplicativo.
• Normalmente contêm apenas propriedades - sem código.
• Usado principalmente para transportar dados de e para um banco de dados.
• As propriedades correspondem exatamente ou estreitamente aos campos em uma tabela específica em um armazenamento de dados.
As tabelas do banco de dados geralmente são normalizadas, portanto, os DTOs também são normalmente normalizados. Isso os torna de uso limitado para apresentação de dados. No entanto, para certas estruturas de dados simples, elas costumam se sair muito bem.
Aqui estão dois exemplos de como os DTOs podem se parecer:
public class Customer
{
public int ID { get; set; }
public string CustomerName { get; set; }
}
public class Order
{
public int ID { get; set; }
public int CustomerID { get; set; }
public DateTime OrderDate { get; set; }
public Decimal OrderAmount { get; set; }
}
Modelos de Apresentação
Um modelo de apresentação é uma classe de utilitário usada para renderizar dados em uma tela ou relatório. Os modelos de apresentação geralmente são usados para modelar estruturas de dados complexas compostas de dados de vários DTOs. Os modelos de apresentação geralmente representam uma visão desnormalizada dos dados.
Características dos modelos de apresentação:
• São objetos de negócios - sua definição depende dos dados do aplicativo.
• Contêm principalmente propriedades. O código normalmente está limitado à formatação de dados ou à conversão de ou para um DTO. Os modelos de apresentação não devem conter lógica de negócios.
• Muitas vezes, apresentam uma visão desnormalizada dos dados. Ou seja, eles geralmente combinam propriedades de vários DTOs.
• Muitas vezes, contêm propriedades de um tipo de base diferente de um DTO. Por exemplo, os valores em dólares podem ser representados como cadeias de caracteres, para que possam conter vírgulas e um símbolo de moeda.
• Frequentemente definido por como eles são usados, bem como por suas características de objeto. Em outras palavras, um DTO simples que é usado como modelo de apoio para renderizar uma grade também é de fato um modelo de apresentação no contexto dessa grade.
Os modelos de apresentação são usados "conforme necessário" e "onde necessário" (enquanto os DTOs geralmente estão vinculados ao esquema do banco de dados). Um modelo de apresentação pode ser usado para modelar dados para uma página inteira, uma grade em uma página ou uma lista suspensa em uma grade em uma página. Os modelos de apresentação geralmente contêm propriedades que são outros modelos de apresentação. Os modelos de apresentação são geralmente construídos para uma finalidade de uso único, como para renderizar uma grade específica em uma única página.
Um modelo de apresentação de exemplo:
public class PresentationOrder
{
public int OrderID { get; set; }
public DateTime OrderDate { get; set; }
public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
public string CustomerName { get; set; }
public Decimal OrderAmount { get; set; }
public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}
Ver modelos
Um modelo de vista é semelhante a um modelo de apresentação, pois é uma classe de suporte para renderizar uma vista. No entanto, é muito diferente de um modelo de apresentação ou de um DTO na forma como ele é construído. Os modelos de exibição geralmente contêm as mesmas propriedades que os modelos de apresentação e os DTOs e, por esse motivo, geralmente são confundidos um pelo outro.
Características dos modelos de exibição:
• A fonte única de dados é usada para renderizar uma página ou tela. Normalmente, isso significa que um modelo de exibição exporá todas as propriedades que qualquer controle na página precisará para se renderizar corretamente. Tornar o modelo de visualização a única fonte de dados para a visualização melhora muito sua capacidade e valor para testes de unidade.
• São objetos compostos que contêm propriedades que consistem em dados do aplicativo e propriedades usadas pelo código do aplicativo. Essa característica é crucial ao projetar o modelo de vista para reutilização e é discutida nos exemplos abaixo.
• Contenha o código do aplicativo. Os modelos de exibição geralmente contêm métodos chamados durante a renderização e quando o usuário está interagindo com a página. Esse código geralmente se refere à manipulação de eventos, animação, visibilidade de controles, estilo etc.
• Contenha o código que chama os serviços comerciais com a finalidade de recuperar dados ou enviá-los para um servidor de banco de dados. Esse código geralmente é colocado por engano em um controlador. A chamada de serviços de negócios de um controlador geralmente limita a utilidade do modelo de exibição para testes de unidade. Para ser claro, os próprios modelos de exibição não devem conter lógica de negócios, mas devem fazer chamadas para serviços que contenham lógica de negócios.
• Muitas vezes, contêm propriedades que são outros modelos de exibição para outras páginas ou telas.
• São escritos "por página" ou "por tela". Um modelo de exibição exclusivo geralmente é escrito para todas as páginas ou telas de um aplicativo.
• Geralmente, derivam de uma classe base, já que a maioria das páginas e telas compartilha propriedades comuns.
Exibir composição do modelo
Conforme declarado anteriormente, os modelos de exibição são objetos compostos, pois combinam propriedades de aplicativos e dados de negócios em um único objeto. Exemplos de propriedades de aplicativos comumente usadas nos modelos de exibição são:
• Propriedades usadas para exibir o estado do aplicativo, como mensagens de erro, nome de usuário, status etc.
• Propriedades usadas para formatar, exibir, estilizar ou animar controles.
• Propriedades usadas para ligação de dados, como objetos de lista e propriedades que mantêm dados intermediários que são inseridos pelo usuário.
Os exemplos a seguir mostram por que a natureza composta dos modelos de vista é importante e como podemos construir melhor um Modelo de Vista que seja eficiente e reutilizável.
Suponha que estamos escrevendo um aplicativo da web. Um dos requisitos do design do aplicativo é que o título da página, o nome do usuário e o nome do aplicativo sejam exibidos em todas as páginas. Se queremos criar uma página para exibir um objeto de ordem de apresentação, podemos modificar o modelo de apresentação da seguinte maneira:
public class PresentationOrder
{
public string PageTitle { get; set; }
public string UserName { get; set; }
public string ApplicationName { get; set; }
public int OrderID { get; set; }
public DateTime OrderDate { get; set; }
public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
public string CustomerName { get; set; }
public Decimal OrderAmount { get; set; }
public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}
Esse design pode funcionar ... mas e se quisermos criar uma página que exibirá uma lista de pedidos? As propriedades PageTitle, UserName e ApplicationName serão repetidas e se tornarão difíceis de trabalhar. Além disso, e se quisermos definir alguma lógica no nível da página no construtor da classe? Não podemos mais fazer isso se criarmos uma instância para cada pedido que será exibido.
Composição sobre herança
Aqui está uma maneira de refazer o modelo de apresentação de pedidos, de forma que ele se torne um modelo de exibição real e seja útil para exibir um único objeto PresentationOrder ou uma coleção de objetos PresentationOrder:
public class PresentationOrderVM
{
// Application properties
public string PageTitle { get; set; }
public string UserName { get; set; }
public string ApplicationName { get; set; }
// Business properties
public PresentationOrder Order { get; set; }
}
public class PresentationOrderVM
{
// Application properties
public string PageTitle { get; set; }
public string UserName { get; set; }
public string ApplicationName { get; set; }
// Business properties
public List<PresentationOrder> Orders { get; set; }
}
Observando as duas classes acima, podemos ver que uma maneira de pensar em um modelo de exibição é que ele é um modelo de apresentação que contém outro modelo de apresentação como uma propriedade. O modelo de apresentação de nível superior (ou seja, modelo de exibição) contém propriedades relevantes para a página ou aplicativo, enquanto o modelo de apresentação (propriedade) contém propriedades relevantes para os dados do aplicativo.
Podemos dar um passo adiante em nosso design e criar uma classe de modelo de vista de base que pode ser usada não apenas para PresentationOrders, mas também para qualquer outra classe:
public class BaseViewModel
{
// Application properties
public string PageTitle { get; set; }
public string UserName { get; set; }
public string ApplicationName { get; set; }
}
Agora podemos simplificar nosso PresentationOrderVM assim:
public class PresentationOrderVM : BaseViewModel
{
// Business properties
public PresentationOrder Order { get; set; }
}
public class PresentationOrderVM : BaseViewModel
{
// Business properties
public List<PresentationOrder> Orders { get; set; }
}
Podemos tornar nosso BaseViewModel ainda mais reutilizável, tornando-o genérico:
public class BaseViewModel<T>
{
// Application properties
public string PageTitle { get; set; }
public string UserName { get; set; }
public string ApplicationName { get; set; }
// Business property
public T BusinessObject { get; set; }
}
Agora, nossas implementações são fáceis:
public class PresentationOrderVM : BaseViewModel<PresentationOrder>
{
// done!
}
public class PresentationOrderVM : BaseViewModel<List<PresentationOrder>>
{
// done!
}