Observações sobre MVP, MVVM e outras letras

David Tiago Conceição
Involves Rocks
Published in
9 min readDec 4, 2017

--

Este post apresenta algumas observações sobre os dois principais modelos de organização da camada de apresentação do mundo Android. Apesar de focar em Android, os conceitos podem facilmente ser portados para outras plataformas.

Há algum tempo o assunto arquitetura vem ganhando atenção no mundo Android. A apresentação dos Android Architecture Components e de um Guia oficial de recomendações de arquitetura trouxe ainda mais atenção para este assunto. Neste post eu apresentarei dois formatos de organização da camada de apresentação muito comuns no mundo Android: Model View Presenter e Model View ViewModel.

Não são arquiteturas

É importante lembrar que MVP e MVVM não são padrões de arquitetura. Ambos são padrões para organização da camada de apresentação. É pouco provável que um aplicativo construído usando apenas estas três camadas de componentes seja um aplicativo de alta qualidade. Por isso, é importante manter em mente que um aplicativo de alta qualidade precisará de outras camadas/componentes/padrões etc.

Mesmo apresentando diferenças fundamentais, tanto o MVP (Model View Presenter) quanto o MVVM (Model View ViewModel) apresentam componentes em comum facilmente identificáveis. Vamos começar entendendo o propósito destes componentes.

Model

Este é o componente que efetua a ligação entre a camada de apresentação e as “regras de negócio” do aplicativo. Ou seja, nesta camada ou em outros componentes acessados por ela, estão todas as validações, verificações, lógicas (e etc.) do aplicativo. Assim, a principal função desta camada é abstrair toda esta implementação, bem como permitir o possível reúso de determinadas implementações em pontos diferentes do aplicativo.

Em geral, espera-se que seja possível testar a camada de modelo com certa facilidade, sem dependências externas para componentes do SDK do Android ou de outra plataforma específica. Isso pode ser possível por meio do isolamento das dependências externas em outros componentes que possam ser injetados ou ter mocks aplicados. Um exemplo deste formato é a abstração do acesso ao banco local por meio de um repositório. Assim, é possível testar o Model utilizando uma implementação mock do repositório.

Existem maneiras diferentes de implementação do Model. Em algumas arquiteturas, esta camada é implementada por Use Cases. Em outras por Interactors. Outras implementações podem também variar o formato da classe de modelo utilizada. O formato exato de implementação não é tão importante, desde que ele siga estes conceitos básicos de camada de modelo.

View

Como o próprio nome sugere, este é o componente de visualização das informações. É o componente que interage com o usuário recebendo ações e interações e renderizando resultados e atualizações. Em geral, esta é a camada mais ligada a componentes externos e com grande dependência do Android SDK ou até mesmo de outras aplicações.

Também pode ser implementada de diversas maneiras nos aplicativos Android: Activities, Fragments e até custom views podem ser usadas para implementar componentes desta camada.

Tanto em MVP quanto em MVVM, a View tem o mesmo propósito. A forma com que ela se comunica com os demais componentes é que varia entre as abordagens. Vamos às variações então.

Presenter

O uso de Presenters é uma das formas de conectar Views e Models, fazendo com que alterações no Model sejam refletivas na View e vice-versa. Neste formato, geralmente o conceito de View passiva é implementado. Ou seja, a View publica métodos que permitem que o Presenter requisite mudanças de seu estado. Todas as mudanças da View devem ser sempre requisitadas pelo Presenter. A View nunca modifica seu estado por conta própria.

Para esta implementação, o Presenter pode conter uma referência para a View. Esta referência é então usada para iniciar possíveis mudanças de estado. A View também pode ter uma referência para o Presenter, através da qual o mesmo é notificado sobre ações do usuário (salvou uma informação, preencheu um valor, etc.).

Visão geral dos componentes no padrão MVP

Uma maneira de evitar o excesso de acoplamento entre Presenter e View é feita por meio da definição de contratos entre os dois. Tanto Presenter quanto View podem ser especificados em interfaces que definem os métodos disponibilizados. Cada componente passa então a referenciar esta interface. Esta abordagem facilita também a aplicação de testes, já que é possível testar ambas as camadas com mocks da outra camada. É importante também que o Presenter não contenha referências para o SDK do Android, já que isso pode atrapalhar a execução dos testes ou mesmo trazer responsabilidades desnecessárias para o Presenter.

Por padrão, o MVP tem alguns pontos fortes. Um dos principais deles é que a implementação costuma ser simples e bastante intuitiva, já que é baseada na chamada de métodos para a troca de informações entre Presenter e View. Os contratos definidos entre os componentes também pode ser usados positivamente, para facilitar os testes e a substituição transparente de um componente por outro que implemente a mesma interface. Também é possível implementar este padrão sem a necessidade de bibliotecas ou componentes externos. Embora estes em geral facilitem o desenvolvimento, eles são opcionais para uma estrutura MVP.

Mesmo com pontos fortes, é importante prestar atenção em alguns pontos do MVP. Um erro muito comum é o excesso de responsabilidades. Um Presenter com muitas validações, “regras de negócio” ou afins, costuma ser um sinal de má aplicação do MVP. Idealmente tudo isto deve ser movido para outros componentes do Model, para que o Presenter tenha apenas a responsabilidade de ligar a tela e o Model.

Em alguns casos, os contratos entre Presenter e View ficam mal definidos, levando a comportamentos indesejados. Por exemplo, se a View Define um método que atualize a tela com uma lista de informações retornada pelo Model, este método não deve remover um possível indicador de progresso da tela. Idealmente, um segundo método deve ser criado para remover ou adicionar o indicador de progresso. Em um outro extremo é necessário evitar a explosão do número de metódos dos contratos. Nem sempre é necessário criar métodos separados para ocultar ou exibir algum componente da tela. Na maioria das vezes, um método que recebe um parâmetro indicando se o componente deve ficar visível ou não pode ser suficiente.

O MVP perdeu algum espaço no mundo Android depois do anúncio do ViewModel do Architecture Componentes, que trouxe alguma atenção para este outro formato.

ViewModel

O ViewModel também conecta o Model e a View mas, em oposição ao Presenter, as implementações de ViewModel seguem o conceito de View reativa. Neste, a View reage a mudanças no estado do ViewModel, sem que este tenha que invocar diretamente métodos da View. O ViewModel não deve sequer possuir referências diretas para a View, embora a View geralmente referencie o ViewModel. Por meio desta referência, a View pode observar os campos necessários e repassar eventos ou ações do usuário para o ViewModel.

Visão geral dos componentes no padrão MVVM

Implementações de ViewModel geralmente utilizam componentes que permitam que campos observáveis sejam publicados para uso da View. Algumas bibliotecas como a de Data Binding ou LiveData permitem que esta conexão seja feita de maneira bastante simples e sem a necessidade de muito código adicional.

Em geral, é possível testar a View e o ViewModel em separado. O ViewModel deve ser facilmente testável, já que não tem referência para a View e, por isso, não precisa de mocks da mesma. A View pode ser testada por meio do uso de um mock do ViewModel com uma implementação específica para os testes.

Da mesma forma que o MVP, o MVVM tem seus pontos positivos. Um deles é que este formato permite a implementação de arquiteturas altamente reativas, em que mudanças no estado de componentes sejam refletidas em outros componentes. Tudo isso de maneira simples e sem a necessidade de escrita de código que invoque diretamente métodos para mudanças de estado. No MVVM também é pouco comum o uso de contratos entre os componentes. Isto simplifica a implementação e evita que sejam necessários diversos passos adicionais em refactors ou mesmo na manutenção dos componentes.

O MVVM também é altamente aderente à proposta do guia de arquitetura publicado pela Google junto com os Architecture Componentes. Há também um componente nomeado ViewModel nos Components. Embora este não tenha sido criado exclusivamente para uso com MVVM, é muito fácil aplicá-lo neste tipo de implementação.

Mas é claro que o MVVM também tem pontos que precisam de atenção. Da mesma forma que os Presenters, os ViewModels podem receber responsabilidades adicionais de maneira incorreta. Lógica, “regras de negócio” e afins, não devem ser colocadas no ViewModel, idealmente sendo movidas para Models. A implementação de MVVM é muito mais propensa ao uso de bibliotecas adicionais como a de Data Binding. É pouco viável e prático implementar MVVM sem estes mecanismos, já que seria necessário escrever todo o código de ligação entre a View e o ViewModel. O ViewModel tende também a ser um pouco mais complexo, já que exige que o desenvolvedor tenha bem claro o conceito de view reativa e como os componentes se relacionam.

Pontos de atenção em comum

Independente de qual opção escolhida, tanto MVP quanto MVVM apresentam diversos pontos que precisam de certa atenção. Um ponto que é constantemente negligenciado pelos desenvolvedores Android é a persistência do estado atual do aplicativo. Como existem diversos eventos que podem levar a tela atual a ser destruída e reconstruída posteriormente, é importante que a estrutura da camada de apresentação seja capaz de recuperar seu estado anterior. Os Android Architecture Componentes resolvem parte desse problema por meio dos ViewModels. Estes são capazes de sobreviver à rotações de tela, um dos eventos que podem causar a destruição de uma View. Mesmo assim, o desenvolvedor deve se preocupar em persistir o estado da tela caso a tela seja destruída quando o usuário sai do aplicativo. Para isto, existem diversas técnicas como persistir as informações combinando os Architecture Components com uma implementação mais completa ou persistir os objetos da camada de modelo para serem recuperados posteriormente.

Outro ponto que merece grande atenção é o isolamento das dependências do framework Android ou de qualquer outro framework usado na camada de View. Para que qualquer uma das duas abordagens tenha sucesso, é fundamental que somente os componentes de View tenham dependências do framework. Caso contrário, os demais componentes podem ficar carregados de responsabilidades desnecessárias, além de serem mais difíceis de testar.

Alguns desenvolvedores defendem também que é importante prestar atenção no overengineering. Este nada mais é que o excesso de preciosismo técnico para uma determinada implementação. Nem sempre são necessários diversos componentes e camadas de apresentação para exibir uma informação para o usuário. É importante ter em mente sempre os propósitos de cada abstração, cada componente e cada camada. Sem isto claro, a solução adotada pode não atender às necessidades ou mesmo atender necessidades que sequer existem.

A nossa escolha

Nos últimos meses nossa equipe têm expandido o uso de módulos MVVM em nosso aplicativo Android. Depois de alguns testes e iterações, ficou evidente que este modelo atende às principais necessidades atuais do nosso projeto. A capacidade de utilizar o framework de Data Binding e a facilidade de compor arquiteturas reativas trouxeram ganhos imediatos à estrutura da camada de apresentação do nosso aplicativo. Ainda estamos iterando alternativas para simplificar nossos ViewModels, aumentar a testabilidade das soluções e entender como integraremos os Architecture Components à nossa solução.

Faça sua escolha

Mas não se limite a uma implementação. Teste alternativas, compare. Assim as diferenças, vantagens e desvantagens ficarão claras. Também ficará claro qual alternativa é mais aderente ao seu determinado cenário. E se for o caso, porque não utilizar as duas abordagens no mesmo projeto?

Fontes e sugestões de leitura

--

--

David Tiago Conceição
Involves Rocks

Android Application Engineer @ Involves; Admin @ Android Dev Br; Organizer @ GDG Floripa www.androiddevbr.org