Trabalhei recentemente num projeto de desenvolvimento de um sistema interno para a empresa, no qual os analistas de suporte, desenvolvimento ou negócios deverão registrar com antecedência as visitas aos clientes e incluir anexos como documentos e e-mails relacionados às visitas.

Tratando-se de um projeto de baixo risco, encontrei uma ótima oportunidade de realizar experimentos arquiteturais e conferir na prática algumas tecnologias que nunca foram usadas por aqui, principalmente relacionadas à arquitetura REST.

O sistema de Agenda de Visitas

O sistema contém uma tela inicial que lista as entradas da agenda, incluindo filtro e ordenação. Os filtros são persistidos em cookies que expiram após alguns dias, então o usuário vê a tela inicial do sistema da mesma forma que havia deixado na última visita.

Para incluir ou alterar uma entrada o usuário deve autenticar-se. Ele somente poderá editar entradas em seu próprio nome a não ser que seja um líder de equipe. Aqui estão requisitos de autenticação e autorização.

Ao incluir ou alterar uma entrada, é possível incluir ou remover anexos, além de alterar sua descrição a qualquer momento.

Um relatório (PDF ou XLS) com as entradas de um período pode ser emitido por qualquer usuário.

O sistema possui log para todas as ações e é possível emitir um relatório de “quem fez o que e aonde” num período de sistema.

sistema-bootstrap

Iniciando com o play! framework

Na primeira iteração do projeto, após a reunião inicial de levantamento de requisitos, adotei o play! framework para criar o primeiro protótipo, o qual fornece uma pilha de tecnologias pré-definidas e promete um desenvolvimento ágil e agradável.

Em geral, gostei bastante do play!. Embora eu acredite ele carece de amadurecimento em muitos aspectos, a codificação e os resultados obtidos são bastante satisfatórios. Depois que se "pega o jeito", novas funcionalidades são agregadas muito rapidamente ao sistema. O framework segue a linha do Rails e utiliza adota fortemente o conceito de CoC (Convention over Configuration – Convenção antes de Configuração), então as aplicações funcionam sem necessidade de criar XML’s ou qualquer configuração complicada. O ponto fraco desse conceito é que se você quiser “fugir” do padrão poderá ter grandes dores de cabeça. A documentação é simples e bem-feita, mas não completa. Precisei andar bastante nos fóruns.

Para as classes de domínio e a persistência das mesmas adotei o padrão DAO com JPA (Hibernate). No mais, os controladores foram feitos no estilo play! e as views em templates Scala (na verdade, um misto de HTML com expressões em linguagem Scala), que são o padrão da versão 2.0 do framework.

A camada de apresentação, renderizada no navegador do usuário, foi implementada em HTML 5 com toques de jQuery. Usei a “estilosa” biblioteca Bootstrap, que fornece componentes visuais muito bons e altamente portáveis entre os diversos navegadores. Nunca me arrependi de tê-la usado.

O desenvolvimento e depuração com o play! merecem destaque, pois o mesmo exibe mensagens muito amigáveis sempre que há problemas em código Java ou nos templates Scala. O resultado nem sempre é perfeito, houve um momento ou outro em que foi difícil achar a causa, mas em geral foi muito mais fácil depurar do que outros frameworks conhecidos.

Os problemas

Após o primeiro ciclo de implementação dos requisitos iniciais, o sistema precisava ser entregue para homologação. Aqui as restrições do play! começaram a surgir. Como toda empresa que trabalha com JEE, usamos como padrão servidores de aplicação como Tomcat, Weblogic e Websphere. Porém, o play! 2.0 não suportava deploy para o formato WAR, embora fosse possível na versão 1.x. As aplicações do play! são distribuídas em pacotes standalone que rodam uma versão do Jetty e escutam por padrão a porta 9000. Para contornar essa limitação criei um manual de instalação exclusivo para essa tecnologia, mas ainda assim houve dificuldade por parte dos analistas de suporte que estão acostumados com servidores de aplicação tradicionais. Além disso, depois de desfeita a confusão, tive que ajudar a configurar a inicialização automática da aplicação num servidor Windows utilizando parâmetros específicos do play! de acesso a banco de dados, porta, etc. Tudo isso poderia ser evitado com a opção de deploy em formato WAR, mas os desenvolvedores optaram por mudar o framework de tal forma que a versão 2 não é compatível com a anterior de forma alguma, nem há um processo natural de migração (upgrade ou downgrade), então descartei essa possibilidade logo de início.

Outra dificuldade foi a falta de uma configuração do path base da aplicação, ou seja, ela só funciona na “raiz” do servidor. O play! gera sempre URLs absolutas. O problema é que a aplicação seria disponibilizada para os diferentes setores da empresa através de um proxy reverso, logo todos os links da aplicação precisariam ser reescritos como http://servidor/aplicacao/. Resolvi a questão usando uma branch do core do framework que permitia alterar a raiz da aplicação através de uma nova configuração e assim os links seriam gerados corretamente. Feito isso, tudo funcionou perfeitamente… pelo menos por um tempo.

A última gota foi quando recebi o retorno da homologação solicitando alterações no sistema (alguns requisitos novos e outros modificados). Parti para a segunda iteração, fiz algumas modificações, mas o play! simplesmente não conseguia mais compilar as classes e views. Depois de algumas horas tentando entender o motivo, descobri que algumas bibliotecas da versão 2.0 foram removidas do repositório Ivy pela própria equipe responsável pelo framework com a justificativa de estarem com problemas. A solução foi migrar para a versão 2.1, porém os problemas com o path base da URL voltaram, pois esta funcionalidade não havia sido incluída na HEAD e não havia uma branch da versão 2.1 com a nova configuração que eu necessitava. O único caminho seria aplicar manualmente as modificações necessárias na versão 2.1 e recompilar o framework… ou mudar de tecnologia.

Brincando com JAX-RS

Em paralelo a essas experiências, também vinha estudando o JAX-RS (Java API for RESTful Web Services) e realizado alguns testes com o framework Jersey. Além de ser uma API bastante fácil de usar, achei promissora em vários sentidos. De certa forma a codificação de controladores em JAX-RS é semelhante ao play! (que também usa REST), o código fica conciso e a assinatura dos métodos é ainda mais flexível.

Decidi apostar numa mudança e, embora ainda enfrentasse o início da curva de aprendizado da API, a migração para JAX-RS com Jersey foi concluída em menos de uma semana. É claro que nem tudo foi um mar de rosas, houveram várias dificuldades. A documentação oficial do Jersey deixa muitos pontos em aberto e a internet está cheia de exemplos defasados pelos fóruns.

Um ponto complicado foram os uploads, pois como já comentei, havia uma funcionalidade de envio de múltiplos arquivos usando o plugin jquery-file-upload e foi difícil encontrar o parâmetro necessário (que é FormDataMultiPart) que funcionasse na versão mais recente do Jersey.

Além disso, tive que estender a classe de tratamento de cookies, mas o culpado (e quase sempre é ele) é o Internet Explorer por não respeitar o cabeçalho MAX-AGE. Outro problema que envolveu o IE e uploads foi relacionado aos nomes dos arquivos enviados para o servidor. O IE envia o caminho absoluto dos arquivos de upload sem “escapar” as barras de diretórios. O resultado obtido pela API do Jersey é algo como c:\diretorio\arquivo.txt. Tanto no caso do cookie quanto do upload, os desenvolvedores do Jersey se negaram a contornar essas situações (leia-se: fazer gambiarras específicas para o IE), logo cabe ao desenvolvedor da aplicação contornar manualmente esses problemas. Pelo menos existem pontos de extensão suficientes para isso, embora carentes de documentação.

No geral fiquei muito satisfeito com o Jersey, principalmente com a possibilidade de estender as classes que permitem transformar o corpo da requisição em objetos Java e vice-versa (usando as interfaces MessageBodyWriter e MessageBodyReader), assim o mapeamento do request para objeto e de objeto para o response fica completamente transparente.

Abandonando JPSs

Outra escolha marcante da segunda iteração foi o uso da biblioteca FreeMarker para escrever a saída em HTML. Encontrei nela uma linguagem de marcação de templates simples, poderosa e extremamente eficiente.

Havia feito algumas pesquisas sobre benchmarks de template engines porque, após conviver bastante tempo com JSP e JSF eu já estava cansado da limitação, da lentidão e das dificuldades encontradas nessas APIs para criar lógicas simples de apresentação. O FreeMarker se mostrou uma das melhores opções, não possuindo dependências, sendo extensível e compacto.

O fato é que o JSF, principalmente nas versões mais novas, até pode já trazer componentes prontos e fáceis de usar, mas na maioria das vezes isso inclui alto grau de acoplamento com a implementação escolhida e com o visual da aplicação. O desenvolvedor que gosta de liberdade e flexibilidade, sem abrir mão da performance, irá preferir uma biblioteca como o FreeMarker ou Grails, que não agregam uma pilha enorme às tecnologias do projeto.

Substituir as marcações Scala pela sintaxe do FreeMarker não foi muito dispendioso depois de ter feito a configuração básica dos templates. A performance é excelente e a maior parte da sintaxe é simples e agradável.

Conclusões

Primeiramente, achei o play! framework promissor, mas em ambientes já dominados por tecnologias JEE ou com determinadas restrições, dificuldades oriundas da imaturidade do projeto podem surgir e acabar com a “mágica” de soluções inovadoras como esta.

A arquitetura REST é excelente para se trabalhar, pois facilita ao desenvolvedor pensar no sistema em termos de serviços. Este é uma das razões que tornaram a migração do play! para o Jersey fácil, pois não foi preciso alterar nenhum URL.

A API JAX-RS, na minha opinião, é uma escolha certeira para a web. Porém, não estou me referindo apenas a enviar e receber dados em JSON ou XML, mas também HTML sobre HTTP.

Não acoplar as diferentes camadas da arquitetura permitiu a troca de tecnologia de forma razoavelmente rápida e com introdução de poucos bugs. Mais um ponto positivo para bibliotecas como o Bootstrap e mais um motivo pelo qual eu não recomendo tecnologias que “amarrem” diferentes as camadas. O planejamento da arquitetura deve levar em consideração o acoplamento entre os componentes utilizados. Se tivesse adotado inicialmente o JSF ou Vaadin, por exemplo, e tentasse trocar de tecnologia, teria que reescrever praticamente a aplicação inteira.

Há frameworks que prometem uma solução completa, uma pilha tecnológica que vai da persistência à visão. Embora sejam bons para desenvolvedores menos experientes ou que simplesmente não querem lidar com decisões arquiteturais, eles não trarão a longo prazo todos os benefícios de uma arquitetura bem pensada e, pelo contrário, poderão se tornar barreiras à evolução do software. A tendência é que a tecnologia estacione no tempo, como é o caso do framework Demoiselle, iniciativa da SERPRO que pretendia criar um modelo completo para o desenvolvimento de aplicações para o governo.

Por outro lado, usar componentes arquiteturais independentes, conectando-os para formar a arquitetura do sistema, apesar de inicialmente ser mais trabalhoso, compensa ao longo do tempo. Uma vez definida uma boa arquitetura básica (por exemplo: persistência, modelo, controle, visão e apresentação) é possível atingir um alto grau de flexibilidade e baixo acoplamento, além de extrair o melhor de cada camada usando a tecnologia adequada e não aquela que vem de “brinde” com o framework. Aliás, essa é a tendência de muitos frameworks modernos. Os desenvolvedores os dividem em módulos e permitem o uso das componentes independentemente uns dos outros. Outra tendência, como no caso do Jersey, que faz mapeamento nativo de e para JSON, XML e JAXB, é o aumento da interoperabilidade e extensibilidade dos frameworks através de APIs e configurações específicas.

Finalmente já é de senso comum que, por melhor que seja um framework, ele não vai atender todos os diferentes requisitos arquiteturais do mercado.