Wire It to me

Como saberão estou – no meu pouco tempo livre – envolvido no projeto MiddleHeaven. Umas das capacidades mais interessantes do Middleheaven é ganhar independência de plataforma através do uso de serviços. Como comentei antes, “serviço” é uma modelo que está em alta e que parece fornecer encapsulamento, isolamento e desacoplamento por definição. Adicionamos a isso a programação para interfaces e os mecanismos de meta-programação (reflection, bytecode-rewrite) e temos um mecanismo muito poderoso para adicionar funcionalidade no sistema.

Ao trabalhar com serviços existem algums detalhes que precisam ser suportados por um framework. Primeiro, tem que existir uma forma de obter o serviço. Tudo bem que não sabemos qual a implementação do serviço – e não nos interessa – mas temos que a obter em algum momento. O padrão básico para isto é o Registry. A idéia é que a implementação é registrada “antes” e “depois” o serviço é recuperado. Esta mecânica existe em vários frameworks baseados em serviços como JMX ou OSGi. Este simples requisito (obter a implementação) é mais complexo do que parece. Acontece que um mesmo serviço pode funcionar diferentemente conforme algum parâmetro e podemos estar interessados em usar mais do que um serviço do mesmo tipo. Por exemplo, um serviço de dicionário tem vários tipos: um para cada língua. Ao pegar o serviço a interface não basta. É preciso (algumas vezes) adicionar algums parâmetros na busca. Dessa forma, é possivel dizer “obtenha a implementação do serviço dicionário para o francês” e depois repetir “obtenha a implementação do serviço para o inglês” e com isso construir um código que testa se uma certa palavra é homografa entre as línguas (como por exemplo weekend). Um mecanismo baseado em serviços tem que ser capaz de suportar diferentes “sabores” de um mesmo serviço. Até aqui parece simples. Basta passar os parâmetros durante a procura. Mas quais parâmetros são permitidos para cada serviço? Precisamos saber? Versão é um parâmetro?

O outro ponto é que usar um Registry para obter os serviços torna o código dependente do próprio registro e da forma de o acessar. Um mecanismo melhor seria usar injeção de dependência. Mas para ser realmente prático, uma implementação de um mecanismo destes teria que ser feito com base em anotações (compare Guice com Spring e veja a diferença entre usar anotações e XML). Isso é interessante porque não mais tenho que me preocupar com o mecanismo de procura , apenas espeficico o que quero. Algo assim:

@Wire @ServiceParams(“language=fr_FR”) Dicionario dicionarioFrances;
@Wire @ServiceParams(“language=en_UK”) Dicionario dicionarioIngles;

O mecanismo de injeção faria algo como

ServiceRegistry.getService(Dicionario.class, serviceProperties )

em que serviceProperties seria um mapa populado pela instrospeção de @ServiceParams.
Um mecanismo de injeção não demora a construir, embora demore a maturar para resolver problemas como referência cíclica e outros. Ou seja, em tese, não é pedir muito que o MiddleHeaven inclua um mecanismo de injeção e permita ao objetos pedir “Hey! MiddleHeaven, wire it to me!”

Tudo muito bem, mas existe ainda um outro fator nesta história. Decidir quais implementações dos serviços devem ser usadas. Esta decisão depende de vários fatores. Serviços de infra como transações e comunicação remota dependem do contexto em que a aplicação corre. Dentro de um container jee é apenas adaptar o serviço provido pelo próprio container, enquanto que em standalone precisamos de implementar o próprio mecanismo ou delegar a alguma biblioteca de terceiros. A aplicação também pode decidir que precisa de um certo serviço ( por exemplo, serviço de reports). Esse serviço tem que estar presente para a aplicação funcionar, mas não é essencial nem utilizado sempre e nem depende do contexto. Além desses, temos serviços externos como serviços de cotação ou B2B que a aplicação usuará.

A este ponto deve estar pensando que já ouvi esta conversa em relação a SOA. SOA é orientado a comunicar sistema fora do mesmo “processo de negocio”. Mas podemos trazer o conceito para dentro e utilizar um design baseado em componentes que funcionam como serviços uns dos outros. Claro que isso é mais facil dito que feito.

Como os serviços necessários à aplicação podem ser adicionadas conforme necessário eles podem também ser removidos ou subtituídos (especialmente os de terceiros) quando necessário. Isto pode significar que depois que a variável foi connectada à implementação, a implementação muda. E muda em runtime. O objeto que a variável aponta não é mais válido. Obviamente isto implica no uso do padrão Observer mas como não há interação com o registro de serviços, o próprio mecanismo de injeção tem que conter alguma lógica especial ou melhor, ser possível injetar alguma lógica especial no mecanismo de injeção😉 . Ou seja, temos que aliar AOP com o mecanismo de injeção para trabalhar com serviços errantes.

Indo mais fundo no problema, a apliação em si mesma pode ser entendida como um conjunto de serviços. Normalmente chamamos as partes de um aplicação de módulos. A idéia é criar uma aplicação criando um conjunto de módulos. Um módulo “principal” contém o coração da aplicação. O que esse módulo faz (o seu serviço) depende de outros módulos e/ou pode ser estendido por outros modulos.

Um exemplo classico são os ERP. Uma compra gera um conjunto de eventos de negócio. Cada módulo (contabilidade, financeiro, etc..) enxerga esse evento de forma diferente e o processa de forma diferente. Para um sistema básico a implementação do módulo pode simplesmente ignorar o evento ou apenas persisti-lo para tratamento posterior. Um módulo mais avançado não apenas produziria informação com base no evento, como ainda disponibilizaria seus serviços para serem consultados por outros modulos/sistemas (via webservice, por exemplo). Um módulo mais avançado poderia alterar o serviço de UI da aplicação para incluir mais telas que permitem ao usuário interagir com o módulo, e alterar o serviço de permissões para restringir o acesso dos usuários. Os módulos seriam então configuradores de serviços, conscientes dos outros módulos de que eles dependem e fornecendo novos serviços para serem consumidos por outros módulos, aplicações ou sistema.

Esta é a idéia por detrás do MiddleHeaven como um framework orientado ao negócio. A aplicação é apenas mais um serviço, composto por outros serviços. O detalhe agora é saber como colocar tudo isto no mesmo caldeirão e funcionar.

A minha idéia era, como comentei antes, utilizar frameworks como o OSGi ou o ainda não estreado Java Module System para cuidar da localização e registro de serviços, mas hoje parece-me que esse mecanismos podem não ser compativeis e/ou não seguir o caminho necessário para o MiddleHeaven. Ou serem mais abrangentes que esse caminho. Enfim, abstrair esses mecanismo parece uma boa idéia para manter a independência do MiddleHeaven mas parece complicado demais…

Deixe uma Resposta

Please log in using one of these methods to post your comment:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão / Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão / Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão / Alterar )

Google+ photo

Está a comentar usando a sua conta Google+ Terminar Sessão / Alterar )

Connecting to %s