View model, domain model, qual model?

Fala, galera

Um dos grandes problemas conceituais ao trabalhar com ASP.NET MVC está na letra “M” de MVC.

A criação de um projeto no Visual Studio do tipo ASP.NET MVC não induz a boas práticas. Temos apenas uma pasta “Models” criada dentro do próprio projeto. Nada de uma sugestão de o que seria o tal “M”.

De fato, o termo “Model” é bastante abrangente e, dependendo do contexto, pode ter um significado diferente. (Outro exemplo é o termo “Serviço”.)

Uma boa prática comumente adotada – por mim, inclusive – é o uso de “view models” na camada de apresentação e um “domain model” para abrigar as regras de negócio da aplicação, na camada de domínio.

Vamos ver do que se trata cada um desses 2 conceitos:

DOMAIN MODEL

– Modelo de objetos – dados e comportamento – que representa o domínio do negócio.

– Forma uma camada isolada das demais camadas da aplicação.  Não é criado levando-se em consideração qual o tipo de interface será utilizado (web, mobile, windows, …), por exemplo.

– Como diz Eric Evans, é o “coração do software”.

VIEW MODEL

– Faz parte da camada de apresentação. Eu particularmente renomeio a pasta “Models” para “ViewModels”.

– Responsável por exibir/coletar dados do usuário.

– Pode conter regras de validação – como de campos obrigatórios – e regras para exibição de informações – como habilitar/desabilitar determinado botão.

– Para cada tipo “view model”, há exatamente uma view fortemente tipada.

SEPARAÇÃO DE RESPONSABILIDADES

No início do texto, mencionei o uso desses conceitos como uma boa prática. Mas afinal, por que seria uma boa prática?

Revendo acima as características de cada conceito, percebemos que suas responsabilidades são totalmente diferentes. Enquanto todo o conhecimento do negócio está no domain model, os view models lidam apenas com preocupações de apresentação.

Essa separação de responsabilidades deixa a aplicação mais organizada e mais fácil de ser compreendida pelos desenvolvedores. Além disso, mantendo responsabilidades de apresentação longe do domínio, podemos utilizar o mesmo domain model para outros tipos de apresentação.

DUPLICAÇÃO DE CÓDIGO?

Você pode estar se perguntando: mas eu não estou duplicando código? Em alguns casos, como em cadastros muito básicos, pode ser que um objeto de domínio seja bem parecido ao view model usado para o cadastro.

Mas volto a repetir, são responsabilidades diferentes! O objeto de domínio terá, além de dados, comportamento específico ao negócio e, portanto, neste caso, não considero como uma duplicação maligna.

MAPEAR DADOS DE UM MODEL PARA OUTRO

“Ok. Mesmo assim, tenho um overhead ao ter que mapear informações de um ou mais objetos do domínio para um view model.” Sim. É o preço a ser pago por essa decisão de arquitetura.

Pode ser, de fato, um trabalho muito chato ficar copiando dados de um objeto de domínio para um view model “na mão” (propriedade por propriedade).

Para facilitar, é comum utilizarmos algum framework para essa cópia de dados, como o AutoMapper.

CONCLUINDO

Vimos, neste post, a diferença entre view model e domain model e quais os ganhos ao se trabalhar com estes dois conceitos em uma aplicação ASP.NET MVC.

Espero que tenham gostado.

Até a próxima!

Anúncios

14 comentários em “View model, domain model, qual model?

  1. Muito legal esse assunto, Robson… Hoje separo meu domain model, mas não de um model view e sim do meu controller, pois é nele que defino, por exemplo, se habilito ou nao um botão. Qual diferença, ou perca, na sua opinião em usar o controller no lugar da view model?
    Abraços!

    1. Olá, Fernando.
      Não é responsabilidade do controller. Ele faz o meio de campo entre a view e o model.
      Em resumo, em uma requisição GET, o controller:
      1) obtém informações do domínio
      2) converte essas informações para um objeto viewmodel
      3) retorna a view adequada (tipada com o viewmodel criado)

      Em uma requisição POST, o controller:
      1) obtém informações da view (usando o model binding para criar um objeto viewmodel)
      2) passa as informações do viewmodel para um ou mais objetos do domínio para realizar alguma operação
      3) instrui o browser a redirecionar para uma pagina de sucesso (RedirectToAction)

      Faço dos controllers os mais enxutos possíveis.
      Qualquer validação/regra de “tela” fica no viewmodel, na própria view (evito muita lógica na view) e com o auxilio de html helpers.

      Espero ter conseguido responder.
      []s

  2. Massa Robson,

    Vou lembrar ainda que o MVC é um padrão de aplicação web, considero que está inteiro na camada da Aplicação. Regras de negócio são da camada de Negócio/Domínio…

    Acho que por isso se faz tanta confusão, mistura-se o “Domain” das regras de negócio com o “Model” do MVC, as vezes pode até ser o mesmo, mas se você precisa de uma visão mais plana (flatten) dos dados para um relatório, não vai colocar isso no domínio do seu negócio, e sim na sua aplicação, que é quem vai gerar o relatório. Não é?

    []’s
    Pablo

    1. Fala, Pablo!
      Como você bem disse, são dados para relatório/consulta, que podem vir de inúmeros objetos, fazer totalizações, etc. Responsabilidades diferentes!

      E sim, o “MVC” todo está na camada de apresentação. Não confundir com camada de aplicação (aplicativo)/serviço, que pode existir entre UI e Domain, para “orquestrar” operações do dominio, transações, autenticação/autorização, etc..

      []s!

    2. Olá Pablo, só para conhecimento MVC não é um padrão web, ele é utilizado na WEB e de certa forma até deturpado devido à questões inerentes ao ambiente e utilização de HTTP. O MVC é bastante antigo e começou a ser usado com a linguagem OO Smalltalk e até onde vejo é mais fácil aplicá-lo “in natura” em um ambiente não-web. Fica a dica para pesquisar sobre “mvc push” e “mvc pull”. O natural do MVC é mais para o “mvc pull” e alguns frameworks component-based até tentam trabalhar desta forma como o JSF. No entanto, no mundo web acredito que não vale lutar contra a natureza do ambiente e entendo que o melhor é usar mvc push.

      Quanto ao relatório, concordo contigo. Acredito que relatório não refletem sempre a visão do negócio conforme os relacionamentos das entidades foram planejados. Relatórios podem ter diversos pontos de vista sobre a mesma informação e serem até “ad-hoc”, ou seja, pra uma necessidade muito específica e/ou sobre um ponto de vista que não tinha sido pensado antes. Com isso, penso relatórios devem ser pensados como um módulo “à parte” do sistema e em muitos casos serem criados nas ferramentas específicas (Ex.: JasperReports, Crystal, etc) com SQL nativo por exemplo.

  3. excelente post, Robson! Só recomendo não usar AutoMapper pois ele é muito lento, ao invés, utilizo ValueInjecter, é mais rápido e não precisa de criar mapeamentos para as classes origem/destino.

  4. Bom post. No mundo java é comum encontrar pessoas chamando este “ViewModel” de “form-bean”, porque bean é um termo familiar para o java. Mas creio que algo como “Form Object” seria uma nome legal. Desassociar VIew de Model é sempre interessante, até em nomes pois pode gerar mais confusão e são camadas com abstrações diferentes.

    Costumo seguir esta abordagem e de fato temos algum nível de duplicação. Isso é um trade-off que deve ser analisado a cada projeto e contexto, mas sem mais informações sobre o mesmo tento seguir essa linha. Para tentar não fugir muito do “DRY” (Don’t Repear Yourself), o mapeamento pode ser feito para as “entidades” no início da requisição e deixar que a model valide de forma única. O que pode ficar para esse “Form Object” seria algo como formatação, máscaras, etc.

    A replicação se justificaria mais num contexto onde se deseja validar algo no client (com javascript por exemplo) para evitar requisições desnecessárias. Alguns frameworks do mundo java, como o Struts Validator, te davam a opção de definir validações em form-beans e execuá-las tanto no server quanto no client (cuspindo o javascript gerado para validar os campos).

  5. Robson, tenho uma dúvida.

    Como eu faço para trabalhar com uma view sem ser tipada, com ela recebendo dois objetos distintos?
    Por exemplo, teria uma view chamada ViewA, dentro dela, teria que receber ObjetoA, e ObjetoB.
    Como a model pode enxergar elas, distinguir uma da outra?
    Poderia ter duas action retornado objetos diferentes para mesma view?

    Estou sempre acompanhado seu blog;
    Obrigado

    1. Olá, Paulo
      Até poderia. A view poderia receber um “object” (eca!!) ou ainda um tipo genérico, mas não vejo muito propósito nisso. Qual seria?
      Se sua view precisa mostrar duas informações diferentes, não seria melhor ter duas views? Cada uma com seu devido conteúdo?
      Menos “macarrão”, o que acha?
      []s

  6. O post é meio velho…
    Mas me responde uma coisa, se puder.

    O conceito de ViewModel, deve ser implementados até mesmo em métodos POST ? em qualquer tipo de model? por mais simples que ele seja ?

    Ou só em casos específicos que há propriedades que não serão mapeadas no model?

    1. Olá, Rodrigo, blz?
      Sim, voce PODE usar a mesma ideia para os POSTs. Nesta caso, ao invés de uma estrutura para exibição, você encapsula a ação que o usuário quer fazer em um “objetinho burro” como a viewmodel (ação esta chamada de “comando” em algumas literaturas). Assim você teria coisas como “EfetuarPagamentoCmd”, “CancelarVendaCmd”, cada um deles com os atributos necessários para a operação em questão.
      Depois, em uma camada adiante, os dados recebidos nesses objetos são utilizados para criar/chamar objetos de domínio (aqueles que contem as regras de negócio da aplicaçao).
      Gosto dessa abordagem.
      Qualquer coisa, da um alo.
      []s

Participe! Vamos trocar uma ideia sobre desenvolvimento de software!

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s