O código de aplicativo complexo escrito sem os padrões MV* é difícil de testar, reutilizar e manter. Os padrões eliminam ou enfraquecem a conexão entre a visualização, o modelo e o controlador, separam o código e simplificam o desenvolvimento. Abordaremos os tipos de padrões MV* e seu uso no SimpleOne.
Ao desenvolver um aplicativo SimpleOne, projetamos cuidadosamente a interface do usuário (UI) e a experiência do usuário (UX) para facilitar e tornar conveniente o uso da plataforma pelos nossos clientes. Mas a UI é apenas a ponta do iceberg vista pelo usuário (User). Por trás dos botões e campos está o código que deve ser dimensionável, sustentável e confiável. Para resolver esses problemas, os desenvolvedores aplicam os padrões MV*, que são usados para separar o código da interface do usuário, a lógica e o processamento de dados.
UI sem padrões
Vamos imaginar um aplicativo que tenha um formulário com vários elementos e widgets. O código desse formulário contém tanto a descrição da lógica quanto o código dos elementos da interface do usuário, e também pode conter fragmentos para processamento de dados. Para um aplicativo simples, o suporte a esse princípio de programação não é um problema. A qualquer momento, você pode encontrar inter-relações e fazer alterações sem quebrar a integridade. Quando o aplicativo se torna mais complexo, o suporte a uma interface escrita sem padrões se torna um problema.
O problema vem da violação do princípio da responsabilidade única (single responsibility principle) – “Uma classe deve ter apenas um motivo para ser alterada”. Temos o código da interface do usuário (View), a lógica (Controller) e o processamento de dados (Model) reunidos em uma interface, portanto, há vários motivos para alterar. Quando alteramos o código de um componente, temos que alterar outros componentes também. Isso complica o suporte ao aplicativo, é quase impossível realizar testes automatizados e a reutilização do código é muito limitada.
É por isso que é conveniente e correto usar padrões – eles separam o código da interface do usuário (visualização), o código da lógica (apresentador, controlador, ViewModel e outros) e o código de processamento de dados (modelo). Podemos alterar facilmente a lógica sem alterar a interface do usuário ou fazer alterações apenas nas rotinas de processamento de dados. Cada padrão pode ser testado independentemente dos outros e usado posteriormente em outros aplicativos.
Os padrões mais comumente usados são Model-View-Controller, Model-View-Presenter e Model-View-View-Model. Vamos dar uma olhada em como eles diferem e onde se aplicam.
Modelo e visualização
Todos os três padrões em consideração têm dois componentes recorrentes. Eles diferem em recursos, mas são iguais em sua essência.
A visualização é uma interface visual (UI). Ela pode consistir em elementos individuais ou widgets. Exemplos de visualização são o código de criação de formulários no MFC e no WinForms, html no ASP.NET, XAML no WPF e no Silverlight.
Model são os dados do aplicativo que são exibidos usando a interface e o processo de recuperação e salvamento desses dados.
Model-View-Controller
O padrão mais antigo, que foi desenvolvido em 1979 para o desenvolvimento de aplicativos em Smalltalk. Naquela época, não havia o shell gráfico do Windows com seus elementos padrão. A interface era renderizada manualmente por código. Entretanto, mesmo naquela época, a separação do código de exibição, lógica e dados foi uma revolução no desenvolvimento.
Nesse padrão, vemos três elementos principais:
- Modelo – dados do aplicativo com a lógica para recuperá-los e salvá-los. Na maioria das vezes, o modelo opera com dados de um banco de dados ou com os resultados de serviços da Web. Os dados são exibidos imediatamente na tela ou adaptados.
- View – interface visual, botões de renderização, rótulos, campos de entrada e outros elementos de formulário. Pode seguir o modelo e exibir dados dele.
- Controlador – monitora as ações do usuário (botões do teclado ou movimentos do mouse), decide o que fazer com elas e atualiza o Model e a View.
Vamos descrever o princípio do padrão MVC da seguinte forma. O controlador processa as ações do usuário – cliques do mouse, pressionamentos do teclado ou solicitações http de entrada. O controlador passa as alterações processadas para o modelo e as desenha na visualização (modo passivo), ou o modelo recebe as alterações diretamente da visualização (modo ativo). A principal tarefa da visualização é renderizar os dados do modelo usando o controlador.
Model-View-Presenter
O desenvolvimento da programação visual e dos widgets aboliu a renderização de elementos individuais da visualização, de modo que uma classe de controlador separada não é mais necessária. Os próprios elementos sabem quais ações o usuário executa com eles. Mas ainda é necessário separar a lógica do aplicativo dos dados. Assim, o padrão foi substituído por Presenter em vez de Controller.
Em comparação com o MVC, a função Model não foi alterada, a View agora lida com as ações do usuário (usando widgets, por exemplo) e, se essa ação alterar algo na lógica da interface, ela será passada para o Presenter.
A principal tarefa desse padrão é separar a visualização do controlador para criar visualizações intercambiáveis e poder testá-las independentemente.
O Presenter, como condutor, é responsável pelo trabalho sincronizado do Modelo e da Visualização. Se ele receber uma notificação da visualização sobre uma ação do usuário, atualizará o modelo e sincronizará as alterações com a visualização. Toda a comunicação ocorre por meio da interface, o que lhes dá separação.
O MVC tem duas implementações: Visualização passiva, em que a visualização não sabe nada sobre o modelo e o apresentador é responsável por obter informações do modelo e atualizar a visualização, e Controlador de supervisão, em que a visualização sabe sobre o modelo e vincula os dados à própria visualização.
Model-View-View-ViewModel
A principal diferença entre esse padrão e os outros é a presença de vinculação de dados no WPF e no Silverlight.
Aqui não há comunicação direta entre ViewModel e View, ela é feita por meio de comandos (vinculação) que consistem em propriedades e métodos. É assim que você pode vincular qualquer visualização e ViewModel, desde que tenha as propriedades corretas. A associação XAML também permite que você associe não apenas dados, mas também ações à visualização. Definimos um objeto como uma propriedade do modelo e o vinculamos declarativamente à propriedade correspondente na visualização. O resultado é um objeto separado que contém dados e comportamento, independente da visualização. O ViewModel é uma combinação de Model e Controller.
As principais vantagens do MVVM são o design fácil da interface, o teste independente e o código reduzido para a visualização.
Otimização e desempenho
Se usarmos os padrões MV* para separar o código da visualização, do modelo e do controlador, em sistemas altamente carregados, será possível obter um benefício adicional ao separar esses elementos em diferentes unidades computacionais.
- A visualização é trazida para o dispositivo do cliente (laptop, PC, smartphone). A tecnologia de aplicativo de página única SPA é responsável pela aceleração. Os cálculos complexos são realizados no endpoint do usuário, reduzindo assim a carga nos servidores de back-end.
- Os controladores (Presenter, ViewModel) são movidos para um servidor de back-end separado que lida com a lógica.
- O modelo é o elemento mais volumoso e produtivo, portanto, requer um servidor com um sistema de armazenamento de dados rápido e, de preferência, com a capacidade de armazenar em cache as informações “quentes” na RAM.
MVC no SimpleOne
Ao desenvolver o ITSM-system SimpleOne, o padrão Model-View-Controller é tomado como base, o que é significativamente otimizado. A função de visualização é desempenhada por um aplicativo SPA de página única, que é executado inteiramente no dispositivo do cliente e recebe apenas dados do modelo. E para a operação do modelo, usamos a abordagem DDD (Domain-Driven Design) – dividimos o padrão em camadas – repositórios por meio dos quais os dados são acessados.
No MVC tradicional, o código do modelo é projetado para um banco de dados específico e sua sintaxe, por exemplo, PostgreSQL. Usando o sistema de repositório, podemos trocar de camadas e conectar qualquer outro banco de dados sem alterar o código do aplicativo.
As camadas nos permitem não apenas trabalhar com repositórios como conectores ao banco de dados, mas também dividir quaisquer classes em entidades separadas. Por exemplo, se em um modelo MVC comum a classe User contém vários milhares de linhas de código e é responsável por todas as operações relacionadas ao usuário (pesquisa, exclusão, envio de mensagens e outras), no SimpleOne nós a dividimos em várias camadas separadas: a classe de descrição do usuário, o repositório do usuário para trabalhar com o banco de dados, as classes de vários serviços relacionados ao usuário e outras. Obtemos uma estrutura MVC, mas mais profundamente desenvolvida, permitindo a troca, uma execução de consulta muito mais rápida e mais fácil de manter.
Conclusão
Os padrões não são paradigmas rígidos que devem ser seguidos para criar uma organização de código perfeita. Eles resolvem problemas muito importantes – enfraquecendo (eliminando) os vínculos entre View, Model e Controller e reduzindo a complexidade do desenvolvimento da interface do usuário. A aplicação dessa abordagem é possível se você entender a essência das interconexões e procurar a possibilidade de eliminá-las em cada projeto específico. O sistema ESM SimpleOne é uma plataforma complexa e de desenvolvimento intenso, mas oferece uma interface amigável e pode lidar com cargas pesadas. Essa eficiência é o resultado do uso de tecnologias e métodos de desenvolvimento modernos, incluindo o uso de padrões MV*.