Aloha senhoras e senhores desta vasta galáxia! (Acredito que não hajam visitantes de outras galáxias neste blog…). Consegui fazer o segundo post deste ano! \o/ Quase não deu tempo…

Bem, há algum tempo precisei desenvolver um projeto consideravelmente grande pensando na modularização dele. Explicando melhor, esse projeto seria implantando em vários clientes, sendo que cada cliente poderia utilizar certos módulos ou não, ou então precisaria de módulos personalizados. Além disso, era imprescindível o início rápido do desenvolvimento impedindo um estudo em cima de padrões de mercado, como o OSGi (hoje obsoleto).

Assim, utilizando o Spring Framework e tirando vantagem das especificações Servlet 3.0 acredito ter conseguido atingir o objetivo com um aceitável incremento na complexidade do projeto. Nesse post tentarei passar o básico das idéias usadas nessa modularização de projeto web permitindo especializações quando necessário, sendo essencialmente features do Spring e da especificação Servlet. No final do post colocarei um link para um projeto de exemplo.

 

MÓDULO?

Antes de mais nada, preciso definir o que eu considero “módulo” ou “modularização”.

Dado um projeto Web com diversas funcionalidades, acredito ser um módulo a reunião das implementações em torno de uma funcionalidade formando uma unidade anexável ao projeto. Assim, tanto essa unidade (na prática um jar) poderá ser anexada adicionando a funcionalidade em questão ao projeto, quanto o módulo poderá ser alterado com impacto mínimo ao restante do projeto.

Exemplificando um pouco, se considerar um projeto de fórum, a publicação de textos poderia ser um módulo. Ou seja, todas as implementações referentes a ele (HTMLs, imagens, javascripts, controladores, persistências e até regras de negócio e validações) ficariam em um jar que seria incluso como dependência do projeto do fórum. Idealmente, na criação de um outro projeto de fórum que também possua a publicação de textos, o módulo já existente poderia ser reaproveitado. Ou então a funcionalidade de publicação de textos poderia ser alterada com mínimo impacto no projeto existente.

Entendido isso, como podemos fazer essa centralização das funcionalidades em um jar? Ou como podemos alterar um módulo sem impacto nos projetos que já o usam? E como se portar diante de seres extradimensionais?

 

MODULARIZANDO RESOURCES

Desde a especificação Servlet 3.0 (Tomcat 7+, Java 6+) os JSPs, HTMLs, Javascripts, imagens e outros recursos podem ser inclusos em jars internos e mesmo assim ficarem disponíveis quando for feito o deploy da aplicação. Basta que sejam inclusos no diretório “META-INF/resources” na raíz do jar.

Na prática, a estrutura do projeto ficará como essa:

META-INF do Base

Quando a aplicação WEB for executada, o conteúdo de “META-INF/resources” ficará disponível como se estivesse na raíz do projeto WEB, juntamente com o index. Ou seja, será possível acessar js/jQuery.js se existir o arquivo META-INF/resources/js/jQuery.js em uma das dependências do projeto.

Dessa forma, já podemos agrupar em pacotes os recursos de acordo com a funcionalidade.

 

ESPECIALIZANDO RESOURCES

Existem casos onde precisamos alterar pontualmente um recurso que está sendo fornecido por uma dependência, seja uma imagem ou um HTML. Podemos sobrescrever esse recurso criando no projeto WEB um novo arquivo como mesmo nome e localização final. O arquivo do projeto WEB sempre terá precedência sobre o arquivo com mesmo nome e localização vindo de uma dependência.

META-INF do Web

 

MODULARIZANDO MANAGERS E CONTROLLERS

Agrupar as classes managers e controladores em jars é trivial. Basta criar os @Controller, @Service ou @Component nos seus pacotes, adicionar como dependência do projeto WEB e o Spring se encarregará de escanear e “pós processar” as anotações. =)

 

ESPECIALIZANDO CONTROLLERS

Para um novo projeto pode ser necessário especializar uma requisição implementada por um módulo. Mas, infelizmente, não é possível sobrescrever um mapeamento de um controlador. A única opção é criar um mapeamento novo ligeiramente diferente.

Por exemplo, considerando o mapeamento “/usuario/lista” que devolve todos os usuários cadastrados, podemos criar um novo mapeamento “/usuario/lista?ativo=true” que deverá retornar apenas os usuários ativos. Assim, a requisição a esse novo mapeamento funcionará com ou sem a “especialização” do controlador (o parâmetro adicional é considerado opcional se não há mapeamento especificando ele).

 

ESPECIALIZANDO MANAGERS

Especializar um @Service ou @Component é mais simples que o anterior. Basta estender a implementação anterior ou reimplementar a interface em questão e utilizar a anotação @Primary. Dentre múltiplos singletons candidatos para a injeção, o @Primary será escolhido.

Assim, seguindo o exemplo anterior, a busca de usuários poderia ser alterada para trazer apenas os ativos através da especialização do UsuarioManager, permitindo manter o controlador inalterado.

Nesse exemplo, o novo manager terá prioridade na injeção do atributo definido através da interface usada em ambas as classes.

Entretanto ainda podemos especificar a implementação antiga caso necessário.

 

Código de Exemplo

Criei rapidamente um projeto de exemplo mostrando esses casos de recursos em dependências e especializações através do Web. E, fazendo proveito do Servlet 3.0, fiz a configuração web do projeto através de classes, ao invés de usar o web.xml.

Vide: https://github.com/jesjobom/modules-web-example

 

Complexidade?

Eu tinha dito que esse tipo de procedimento para modularizar funcionalidades criava uma complexidade adicional.

Bem, basicamente essa complexidade reside na necessidade de rastrear o impacto de qualquer alteração em algum módulo já utilizado por vários projetos. Alguma organização ou versionamento dos códigos se fazem necessários a fim de manter os códigos antigos estáveis.

Além disso, pode haver confusão quando certos recursos forem especializados enquanto outros não.

Bem, por hoje isso é tudo, pessoal. Volto ano que vem! …ou depois dele…