Mais lamúrias sobre frameworks Web
Continuando a fuçar "onde foi que eu errei" a respeito do Luca e dos frameworks para Web, cheguei a algumas conclusões parciais.
Primeiro, durante o desenvolvimento do Luca apareceu um pattern interessante: a "representação de dados" em formatos diversos, e a conversão de uma representação para outra por meio de filtros. O Luca lida com dados em 3 formatos distintos:
Representação SQL (RS): É a que pode ser oferecida diretamente ao Model, no caso o SQLObject. Por conseqüência, é praticamente pareada com a estrutura da tabela SQL. Também é o formato obtido quando se puxa dados via SQLObject.
Representação intermediária (RI): O controlador só manipula e valida dados nesse formato, portanto pode ser considerado o "principal". As demais representações só existem para satisfazer outros componentes.
Representação Web (RW): Formato que visa satisfazer ao template XML. Também é o formato que é obtido quando o usuário submete dados via formulário Web.
O controlador deve implementar os filtros de conversão entre RS, RI e RW,, mas ele herda filtros default suficientemente poderosos para as conversões triviais, de strings e inteiros, que são a maioria. Apenas os dados "excepcionais", cuja representação no SQL não tenha nada a ver com o apresentado na página HTML, precisam ser convertidos pelo controlador.
Com isso eu tentei isolar as "regras de negócio" do controlador ao máximo, tornando-as independentes tanto dos detalhes desagradáveis do SQLObject quanto dos dados vindos diretamente da Web (que podem estar sujos ou num formato não ideal). Também tentei economizar código, de modo que especificando as colunas numa única tabela molda todo o comportamento do controlador, evitando ter de repetir as mesmas variáveis um monte de vezes.
Infelizmente, as colunas ainda precisam ser especificadas novamente nos templates XML e nos validadores Turbogears (que no Luca são utilizados para apenas converter tipos, não para a validação completa). Este fato é uma das minhas principais broncas.
Então, pensando em termos de processamento de dados, tudo que é mostrado na Web vem de um banco de dados. De forma simétrica, tudo que o usuário faz no sistema tem como único objetivo final gravar dados no banco. Entra porco, sai lingüiça. O fluxo de dados de SQL para HTML é:
SQL -> Model -> RS -> Filtro SI -> RI -> Filtro IW -> RW -> XML -> HTML
O fluxo de dados do formulário até o SQL é um pouco diferente, na ponta Web:
Form -> Validador TG -> RW -> Filtro IW -> RI -> Filtro SI -> RS -> Model -> SQL
O problema da promiscuidade entre Model, View e Controller é que quase todos os blocos acima estão contidos no Controller: RS, Filtro SI, Ri, Filtro IW, RW e validador TG. O Controller está trabalhando demais.
O Model deveria, além de esconder o banco de dados SQL, também implementar a representação intermediária (RI). Ou seja, ele deveria fornecer e aceitar dados no formato predileto do Controller, inclusive fazendo a validação. O Model ideal é muito mais que um simples mapeamento SQL-objeto. Isso encurta o fluxo para:
Model -> RI -> Filtro IW -> RW -> XML -> HTML
O próximo problema é a representação Web. Como os templates XML são linguagens canhestras, o Controller tem de trabalhar mais, oferecendo dados "pré-mastigados" ao template. Daí a necessidade do filtro IW.
A rigor esse filtro deveria estar numa classe View separada do Controller; o que não é possível no Turbogears porque o View é apenas o mecanismo de template XML, e escrever um View "completo" exige código de verdade. Nesse cenário ideal, o fluxo fica
Model -> RI -> View{Filtro IW, RW, XML, HTML}
Até aqui, "consertar" o Luca demandaria as seguintes providências:
* Escrever Models melhores, que isolem completamente o SQLObject do resto da aplicação e interfaceiem dados no formato predileto dos Controllers -- o RI.
* Escrever decorators View mais poderosos, que aceitem diretamente dados RI e virem-se para transformar dados "puros" numa página Web.
Mas a contaminação dos Controllers não se resume ao fluxo de dados -- ela também ocorre nas chamadas a métodos. Por que?
Porque é justamente esta a idéia do CherryPy, componente do Turbogears: se você invoca uma página bla.com.br/bla/ble, isso provoca a chamada a um método "ble" do objeto Controller "bla". Sem dúvida é uma coisa boa, mas talvez o objeto "bla" não devesse ser um Controller no sentido MVC do termo...
Se os métodos do Controller são moldados de acordo com as páginas Web, é óbvio que o controlador vai ficar viciado com o jeito Web de ser. Se for necessária qualquer outra interface que não Web, já vai ficar complicado ou impossível usá-lo para atender outros tipos de cliente, como texto, GUI... ou mesmo AJAX.
O cliente AJAX é um caso interessante porque do ponto de vista do controlador ele não é Web, pois o cliente AJAX troca dados puros com o controlador (codificados em JSON), e não páginas ou formulários. Um controlador "viciado" para Web não servirá nem para AJAX. Será preciso criar novos métodos.
É exatamente o que acontece no Luca: há métodos JSON para as páginas AJAX, e há métodos de páginas Web para as páginas sem AJAX (utilizáveis em navegadores antigos ou incompatíveis). Funciona, mas duplica algum código e mistura conceitos.
O ideal seria haver um pré-controlador para Web, intimamente ligado ao View, que atendesse as páginas Web normais, e este por sua vez refurmulasse a requisição controlador "puro", agnóstico ao View, e implementa apenas as regras de negócio. Já as requisições JSON poderiam ir direto ao controlador "puro", pois trafegam dados puros (e podem servir a clientes GUI).
No Luca, há apenas um controlador "puro", que é responsável por comunicar-se com o celular (assim eu posso registrar minhas despesas no celular e fazer upload via Bluetooth). Por sua vez, este controlador invoca métodos de outros, pois diversas transações diferentes são levadas a cabo a cada sincronização celular-servidor. (Foi fácil chamar métodos de outros controladores pois todos já tinham suporte a AJAX).
Em resumo:
* No Turbogears e aparentemente nos demais frameworks, induz-se poluição dos Controllers devido à anemia dos Models e dos Views;
* Os Models "sugeridos" pelos frameworks são fracos pois são apenas uma casca fina sobre SQL, embora seja fácil implementar e usar Models mais adequados.
* Os Views dos frameworks são fracos pois restringem-se a encoders, como os templates XML, quando deveriam ser classes Python normais (e estas sim, por sua vez, poderiam usar algum mecanismo de template XML).
* Talvez eu esteja exigindo do Turbogears que sejam servidores de aplicação, não simples um framework Web que é afinal a sua proposta. (Mas, na minha opinião, um sistema AJAX tem características não-Web.)
* Minhas queixas contra o MVC estavam erradas, o problema dos frameworks poderia ser a má interpretação do pattern MVC.


0 Comentários:
Postar um comentário
<< Início