Faz sentido distribuir tarefas entre os componentes do MVC?

Publicado em 2018-03-29 por Vinicius Assef

Outro dia um amigo me enviou uma pergunta bem interssante por email. O assunto era sobre separação de responsabilidades numa arquitetura MVC.

Oi Vinicius,

Como estão as coisas? Espero que tudo bem e que aproveite o feriado...

A pergunta não me parece tão simples de ser respondida, só não queria receber um "depende"... heheheh

Vamos começar pelo VIEW que é a parte (grupo) do sistema responsável pela visualisação e só e somente só visualisação.... Sem perguntas.

Pulamos então para o CONTROLER que fica responsável por comunicar com o VIEW e com MODEL... aqui caberá uma pergunta

E por fim o MODEL que é a modelagem do ??????

Pronto a pergunta está aqui... Que a modelagem do banco de dados fica aí eu não tenho dúvidas... Mas... Eu vi um exemplo onde a inteligência do sistema também ficava no MODEL e o CONTROLER apenas pegava o que o usuário colocou na UI (VIEW) mandava para o pro MODEL, recebia a resposta do MODEL e mandava de volta para o VIEW.

Isso faz sentido com BD... Muito sentido! Mas o exemplo era de uma calculadora :)

Ou seja, neste exemplo o MODEL era quem somava 1 + 1 :)

Sei que num exemplo deste o CONTROLER poderia fazer tudo... Mas este tipo de distribuição de funções (tarefas) faz realmente sentido?

O MODEL tem que responder para o CONTROLER tudo mastigado?

Exemplo: [FUNCIONARIO] ------- [DEPTO]

opção 1 ) CONTROLER faz a query para o MODEL (select * from FUNCIONARIO where depto =1;)

opção 2 ) CONTROLER envia a query (digo envia a string da query, por exemplo) para o MODEL e espera que este último envie de volta a resposta.

Qual faz mais sentido?

Vamos por partes.

Um dos benefícios do paradigma MVC é promover desacoplamento. Certo, mas desacoplamento entre o quê? Entre as partes do sistema, que normalmente chamamos de camadas.

Essa nomenclatura (MVC) nos induz a pensar que o sistema só tem essas 3 partes (camadas), o que não é verdade.

Vou escrever um pouco sobre algumas partes que quase todo sistema tem, começando pela mais simples.

A view: aqui, o ideal é termos apenas lógica para visualização e interação com o usuário. Comumente em sistemas web associamos a camada view com html, mas nem sempre é assim. Por exemplo, se nosso sistema responde um json, a lógica para serializar os objetos json estaria na view. Se devemos responder um PDF, a lógica para gerar o PDF também deve ficar na view. Como a view é para visualização, ela precisa receber os dados de alguém; o que nos leva ao controller e a uma discussão conceitual.

O controller: alguns defendem o controller nos moldes do web2py, outros, nos moldes do django. Falando rapidamente no django, ele diz que o controller é o sistema de rotas dele e o que ele chama de "view" é o que conhecemos normalmente como "controller". Sem entrar em muitos detalhes dessa abordagem do django, o controller é a "cola" entre o seu sistema e a visualização dos dados para que o usuário possa ter uma interação. Posto isto (e lembrando do tal desacoplamento que citei no início), temos que o controller precisa tomar conta da navegação: qual mensagem mandar a view mostrar; pedir para o usuário logar porque tal funcionalidade é protegida por senha; redirecionar o usuário para outra "tela" depois que realizar uma ação; etc. e, "last but not least", escolher qual view invocar para mostrar o resultado para o usuário. Tendo em mente que o controller tem essa responsabilidade de interação, precisamos de alguma parte no sistema que lide com os dados, o que nos leva a outra parte (camada), o "negócio" (business).

O negócio: falamos em como os dados são apresentados e como a navegação deve ser controlada, mas ainda não tratamos de lógica de negócio. Por exemplo, se estamos incluindo um novo registro, quais os campos são obrigatórios? Quais as regras para calcular o valor a ser pago por um produto? Este usuário tem algum desconto? Isso não faz parte da view e nem do controller; faz parte do negócio. No seu exemplo da calculadora, aqui estariam as suas regras pra fazer os cálculos, de acordo com as opções escolhidas pelo usuário, na view. É importante notar que a camada de negócio não poderia saber detalhes da interação com usuário. Por exemplo, essa camada não deveria gerar trechos de html (porque a visualização pode ser em json). Então, a comunicação com o controller tem que ser independente da view. Ou seja, a camada de negócio não deve tratar um request, por exemplo. Continuando, em geral o negócio precisa de dados para funcionar, o que nos leva a outra parte (camada), dados.

Os dados: também conhecida como camada de persistência, ou repositório, ou, mais comumente de acesso a dados. Em java é comum vermos DAO (data access objects). Aqui é a parte do sistema que "lida" com os dados, esses "animais incríveis e onde eles habitam". Quando pensamos nessa camada somos tentados a achar que "pronto, meu banco de dados", mas "isso é uma cilada, Bino!". Os dados podem estar em arquivos csv, txt, banco de dados, sistemas externos, e por aí vai. Além disso, pode ser necessário juntar dados de origens diferentes para que ele faça sentido para o sistema. Exemplo: para saber se um usuário pode acessar o sistema, seria bom que soubéssemos que ele não está de férias, ou que ainda faz parte do quadro de funcionários. Portanto, precisaríamos consultar o sistema de pessoal para chegar a essa conclusão. Então, a camada de dados divide-se em duas: representação e acesso. A sub-camada de representação deveria montar as estruturas de dados que fazem sentido para o sistema, enquanto a sub-camada de acesso faz os malabarismos para acessar os dados, seja lendo um csv, chamando uma API do sistema de pessoal, buscando dados em uma pág web, ou simplesmente acessando seu próprio BD. O que os ORMs dos frameworks têm, em geral, é essa subcamada de acesso aos dados. Primeiro, porque uma classe mapeia diretamente para uma tabela do BD. Segundo, porque ele nos fornece uma abstração para acessar os dados (Produto.objects.all() ao invés de um SELECT). Dependendo de o quão fundo você queira ir nesse lance de desacoplar, ainda podemos ter uma camada de conectores.

Os conectores: se seus dados residem um um sistema externo, seria bom ter uma parte do seu sistema responsável por conversar com ele. Por exemplo, chamar e entender as repostas do Twitter.

Voltando à nossa realidade, usamos frameworks que simplificam, misturam e têm atalhos pra tudo isso, mas acoplam as partes umas nas outras.

O que os frameworks MVC chamam de model, na realidade, é uma mistura grotesca entre a camada de negócio e a sub-camada de acesso a dados!

O ideal é que a camada de dados forneça uma abstração sobre a forma que os dados são persistidos. Por exemplo, o sistema implementa os itens em um bd relacional normalizado, mas pode mudar para uma estrutura desnormalizada e isso ficar transparente para a camada de negócio.

Enfim, sim, faz todo o sentido em forçar o controller a chamar o "model", mesmo para tarefas que hoje são simples.

Mas, pisando no chão, pode ser que seu sistema continue simples para sempre ou que ele não seja feito para durar muito tempo. Aí você pode quebrar as regras e ser mais pragmático. No entanto é importante conhecer as regras para saber quais e porquê você está quebrando.

Vinicius Assef

Eu sou apaixonado por Python e shell script.

Aprenda com seus erros e dê nome certo às coisas.