VIPER — Arquitetura limpa em nossos APPs

Ronan Rodrigo Nunes
Jurassic Park Hackers
8 min readJul 20, 2016

Gosto da analogia entre software e carro. No carro assim como em um software temos camadas de componentes que são separados por formas de utilização. Um dos carros que mais me atrai é um Locost Seven. Ele é um carro de corrida inspirado no Lotus Seven e seu projeto é aberto para construção caseira (não que isso seja fácil).

Locost Seven — http://www.cheapsportscar.net

Logo de cara, a carcaça externa ou lataria pode ser considerada a tela do nosso sistema. No carro ela define um estilo e propósito, basta olhar o Locost e perceber que ele é um carro de corrida com apelo vintage. No software essa parte é representada pelas telas que podem transmitir a real intenção de cada parte do sistema.

Por baixo da carcaça existe uma estrutura base, diferente e essencial para cada carro. Quando carro de corrida, necessita de uma estrutura forte para aguentar disputas, mas ao mesmo tempo leve para ter um bom desempenho. No caso do Locost, além de ter uma estrutura pensada para corridas, seu chassi interno pode ser amplamente modificado.

O que mais atrai no projeto do Locost é facilidade de modificações. Por exemplo, a versão brasileira do Locost foi facilmente adaptada pra comportar um motor de Chevette e peças de Fusca. E isso só é possível pois foi concebido tendo uma estrutura base que comporte modificações. Um software também deveria ser assim. Ter uma estrutura que isole as camadas, facilitará a troca por exemplo do Parse, serviço de push notifications que o Facebook matou, por Firebase.

Instrumentos de um carro

Assim como num software, um carro é repleto de instrumentos de ação (pedais, alavancas, botões, etc.). Também possui diversos mostradores que dão um feedback do que está acontecendo no sistema. Essas são as camadas de interação, que também devem ter seu devido isolamento, para que a substituição dos componentes nessa camada sejam fáceis e não afetem todo o sistema. A troca de um velocímetro por outro não deve fazer o motor funcionar diferente, ou até mesmo parar de funcionar. E para proteger nossos sistemas das mudanças de detalhes, existe a camada de Frameworks & Drivers.

O motor é o responsável por transformar um combustível em energia mecânica. No software isso ocorre quando o usuário preenche um formulário, e os dados preenchidos precisam ser convertidos numa estrutura que facilite interações no sistema. Outro ponto de transformação é quando o aplicativo solicita dados de um banco de dados ou de um serviço web e também transforma estes dados numa estrutura que faça sentido dentro do software. E a camada que realiza esse serviço é conhecida como Interface Adapters.

A roda é algo comum entre todos os carros. É possível pegar a roda de um carro e encaixar em outro. E embora haja diferença entre algumas furações, existem adaptadores para colocar uma roda de 5 furos num encaixe de 4 furos. Em um sistema teremos códigos que podem ser reaproveitados em outros softwares e devem estar numa camada isolada chamada Entities.

Usuário.

Para o motorista, disponibilizamos tudo o que é necessário para executar a principal interação ou caso de uso, dirigir. E dirigir um carro possui estranhas semelhanças com cadastrar um cliente. São semelhantes por envolverem um usuário, interação com um sistema, processamento das informações, validação de regras (de negócio) e feedback do que está acontecendo. Os casos de uso de um sistema devem morar na camada Application Business Rules.

Locost Seven é um carro pronto para mudanças e melhorias. Tem componentes isolados e com responsabilidades bem definidas. Assim tem que ser nosso Software, e é por isso que devemos esquecer, pelo menos um pouco, o MVC.

Tchau MVC

Meados de 2014 eu comecei a estudar sobre desenvolvimento de aplicativos para iOS. Fazia parte do meu estudo baixar APPs de código aberto e essas aplicações tinham sempre duas características. Primeiro que era utilizado MVC e segundo que nunca possuíam testes.

Por utilizar o padrão MVC, as vezes de forma errada, acabava-se criando arquivos enormes. Quando eu via que a classe herdava de UITableViewController eu tinha a certeza que estava na lama e tinha que aprender a nadar. Eram classes que exerciam muitas responsabilidades, desde gestão das views até interações nas camadas de persistência.

E a falta de testes era uma consequência do Massive View Controller. Ficava difícil testar unitariamente uma classe gigantesca, pregada ao framework e à camadas de persistência.

Os dias de MVC estavam contados. Metade de 2015 eu comecei a trabalhar na Meus Pedidos, e foi lá que tive meu primeiro contato com a Clean Architecture. Confesso que minhas primeiras impressões não foram das melhores. Mas conforme o tempo foi passando eu fui me interessando, compreendendo e vendo que o mundo poderia ser um lugar melhor. Chega de lama.

Então comecei a criar APPs para estudo me baseando na arquitetura que utilizávamos no projeto web da Meus Pedidos. Ao mesmo tempo, comecei a pesquisar sobre arquiteturas utilizadas no desenvolvimento de APPs. Até encontrar a VIPER, que é uma forma de aplicar as camadas propostas pela Clean Architecture no mundo mobile.

Monster (Massive) View Controller e suas tretas

Clean Architecture

É uma proposta de arquitetura de software criada por Uncle Bob (Robert C. Martin). Inspirada em outras 5: Hexagonal Architecture, Onion Architecture, Screaming Architecture, DCI e BCE.

A Clean Architecture divide nosso software em camadas. As camadas auxiliam a criar um software que esteja de acordo com os 5 princípios do SOLID. E também existem para organizar os códigos dos nossos APPs.

As camadas da Clean Architecture

Camadas propostas pela Clean Architecture

Frameworks & Drivers

É a camada mais externa, uma camada repleta de detalhes. São detalhes pois a alteração de suas partes não altera o comportamento do sistema. O aplicativo continua fazendo o que ele foi projetado para fazer, independente das escolhas feitas nessa camada.

Interface Adapters

Aqui é onde moram os responsáveis por transformarem dados. É a casa dos presenters, controllers, gateways e repositories. Os dados digitados num formulário, vão ser recebidos nessa camada, convertidos em um Value Object, e passados para a camada seguinte.

Motores realizam um trabalho extremamente semelhante. Através de mangueiras, recebem combustível e transformam em energia mecânica, que é aplicada nas engrenagens que colocam o carro em movimento.

Application Business Rules

Nesta camada é onde deve ficar as lógicas e regras de negócio que darão vida ao nosso software. Dentro desse pacote, encontra-se os Use Cases ou Interactors, que são os detentores das regras de negócio.

Enterprise Business Rules

Aqui deve entrar códigos de regra de negócio à nível de negócio. Esses códigos devem estar prontos para o reaproveitamento. Uma regra para aplicar desconto sobre desconto se encaixa perfeitamente aqui. Pois ela pode ser reutilizada em diversos use cases/interactors.

No mundo automobilístico quando giramos um volante sentido horário o veículo vai para a direita. Se viramos anti-horário o veículo vai para a esquerda. E isso não importa se você está dirigindo um carro de corrida, de passeio, caminhão, ônibus… Se tiver volante, a regra ta valendo.

VIPER

Diagrama de como o viper interage entre as camadas

Sendo o VIPER uma forma de usar a Clean Architecture, ele sugere como fazer as interações entre as camadas propostas por Uncle Bob. Na imagem acima cada componente proposto pela VIPER está pintado conforme sua respectiva camada.

VIPER é um acronimo formado pelas seguintes palavras
View — Telas
Interactor — Regras de negócio à nível de aplicação
Presenter — Transformam dados para serem apresentados ou encaminhados para o interactor.
Entity — Value objects ou lógicas reaproveitáveis
Router — Cuida do fluxo de telas

Hora do código

Criei um aplicativo para iOS com o intuito de demonstrar e estudar VIPER. Fique a vontade para contribuir com o projeto. Seja através de uma Pull Request ou reportando algo através de Issue.

Aplicativo construído para demonstrar o uso de VIPER — https://github.com/ronanrodrigo/my-customers

View

Código que está na View Controller e possui a função que será disparada pelo evento de toque no botão de salvar.

Método de toque num botão na View Controller

Presenter de Input

Código que vai instanciar o interactor injetando as dependências necessárias através do construtor e chamar o método principal do interactor.

Presenter de Input, transforma dados e chama o interactor

Interactor

Código que gosto de construir com base no Command Pattern. Onde existe um único método público. Que vai fazer o que o nome do interactor sugere.

Presenter de Output

Em alguns casos poderá existir somente o presenter de output. Como por exemplo o utilizado para mostrar a lista de todos os clientes. Assim que o usuário abre o aplicativo é chamado um interactor que busca a lista de clientes, e devolve através de um presenter de output.

Router

No caso do iOS ele vai sempre realizar alguma interação com o framework na parte de telas.

E agora…

Ufa, depois disso tudo podemos ver que o nosso sistema está se encaminhando para ficar muito parecido com um Locost Seven.

Agora fica fácil, conhecer bem o projeto pois cada camada está isolada fisicamente, ficando fácil de navegar na estrutura do projeto.

Facinho pra reaproveitar código até mesmo em outros projetos. Ja trabalhei em projetos onde tínhamos um projeto Core, onde moram use cases e entities, que são compartilhados entre projetos Android e iOS.

Independência de framework e serviços. Assim como o Parse já morreu, qualquer outro serviço pode morrer. E isso não será mais uma dor de cabeça. A utilização desse serviço vai estar numa camada isolada e fácil de trocar.

Testar fica prazeroso. Acabou a dor de saber como realizar setup de banco, View Controller, Storyboard e etc pra rodar testes unitários. Injete um gateway/repository que funciona com dados em memória pro use case/interactor e um presenter fake que ta tudo resolvido.

Agora o designer do time pode remodelar o sistema inteiro, pois mexendo nas telas, as regras de negócio continuarão intactas.

Migrar um banco de dados vai sempre doer. Mas aqui conseguimos diminuir um pouco da dor. Os locais à serem alterados após a migração são somente os repositories/gateways. Chega de caçar interações com banco dentro de models e controllers.

Referência bibliográfica

Projeto de exemplo

Talks

Fiz duas talks sobre esse tema, uma no FISL e outra no TDC de Porto Alegre. Os slides podem ser acessados em: https://speakerdeck.com/ronanrodrigo/viper-arquitetura-limpa-em-nossos-apps

--

--