Metas e Princípios de Arquitetura

O framework Jmine foi construído tendo como objetivo ser uma plataforma tecnológica adequada para o desenvolvimento de aplicações corporativas de grande porte, em especial soluções de back office. Apesar de não haver nada que impeça sua utilização em outros contextos, tenha em mente que podem ser herdados controles desnecessários para seu caso ou faltem recursos presentes em outros frameworks.

Aplicações de back office corporativo possuem diversos requisitos não funcionais que possuem forte influência na arquitetura, o que direcionou diversas escolhas na construção do Jmine. A proposta é que uma aplicação construída em cima do framework, além de seguindo certos conceitos e regras, atinja estes requisitos. Abaixo temos alguns dos requisitos principais e o racional para sua existência, seguido de um detalhamento de como são atingidos.

  1. Integridade dos dados - soluções de back office são partes centrais da TI de grandes corporações, pois nessas soluções em que ficam armazenados os dados oficiais de operação da instituição. Erros e inconsistências nesses dados podem gerar grandes prejuízos, por exemplo através de erros operacionais envolvendo pagamentos, ou até mesmo a geração de informes legais errados ao governo, o que pode caracterizar fraude. Manter dados íntegros, portanto, é uma preocupação central.
  2. Alto desempenho - tipicamente corporações adotam soluções para operacionalizar trabalhos antes feitos manualmente, ou através de planilhas, visando ganhar tempo e eficiência. Um sistema lento atrapalha a eficiência e não permite que um grande número de operações seja feito, prejudicando a operação da instituição.
  3. Escalabilidade - muitas vezes a capacidade de realização de operações é um ponto limitante no número de negócios que uma instituição pode fazer: oportunidades podem ser perdidas por que o limite operacional do back office não suportaria a carga adicional de trabalho. Boas soluções de back office devem ser capaz de escalarem rapidamente sua capacidade de processamento, através da instalação em servidores mais potentes e/ou utilizando clusters de servidores, permitindo a expansão dos negócios.
  4. Alta disponibilidade - é comum que soluções de back office são necessárias para o funcionamento da instituição: downtime é um grande prejuízo pois neste tempo os negócios estão paralizados, o que pode gerar perda de oportunidades, perda de clientes e mesmo multas. A alta disponibilidade e tolerância a falhas são, portanto, um fator altamente desejável.
  5. Segurança - os dados deste tipo de sistema são quase sempre confidenciais, além de muitas vezes lidarem diretamente com movimentações financeiras e podem vir a ser utilizados por usuários mal intencionados para favorecer terceiros. A segurança da aplicação deve, portanto, prevenir tanto ataques externos quanto prevenir mal uso, intencional ou não, por parte dos próprios usuários.
  6. Portabilidade - por todas essas características essas soluções são consideradas críticas à sobrevivência da instituição, que tomará diversas atitudes para se proteger, dificilmente abrindo mão de que a instalação seja feita nos próprios servidores, onde ela tenha total liberdade para garantir a segurança como achar melhor. Restringir o ambiente para deploy limitaria seriamente o número de clientes em que sua aplicação poderá ser instalada, portanto precauções devem ser tomadas para tornar a aplicação altamente portável.

Integridade

A arquitetura da solução deve garantir a integridade dos dados, garantindo a atomicidade e isolamento de todas as transações.

Primeiramente, toda operação deve ocorrer no escopo de uma transação, o que garante a atomicidade e isolamento, o que juntamente com validações adequadas e controles de concorrência garantem a integridade.

Para controle de concorrência deve ser feito uso preferencial da estratégia de lock otimista em banco de dados. Nesta estratégia todo registro que pode ser atualizado de forma concorrente possui um número de versão, que é incrementado com cada atualização, e cada atualização é feita indicando a chave do registro e a versão que está sendo atualizada. Em casos em que colisões são raras esta estratégia possui alto desempenho e muito baixa retenção, colaborando com a escalabilidade da solução. No caso de alteração concorrente haverá uma falha em uma das transações, que deverá ser reexecutada.

O controle de concorrência deve ser feito com lock pessimista explícito no banco de dados no caso de registros atualizados de forma concorrente com muita frequência, pois nesse caso haveriam muitas falhas e tentativas subsequentes, o que prejudicaria o desempenho. Situações que requerem esse tipo de lock devem ser poucas, e o lock mantido pelo mínimo tempo possível para evitar retenção.

Sempre que possível o próprio modelo de dados deve garantir a sua integridade, fazendo uso de definições como unique constraints e check constraints no próprio banco de dados.

No caso do consumo de mensagens de um Message Oriented Middleware (MoM), a transação de consumo da mensagem e a transação da efetivação da operação em banco de dados devem ser coordenadas em uma única transação distribuída através do protocolo XA, que garante a atomicidade e integridade da operação. Esse controle é vital para evitar que mensagens sejam perdidas ou duplicadas, situação que pode acontecer no caso de falhas quando não há coordenação das transações.

Alto desempenho

A solução deve ter alto desempenho, fazendo uso racional dos recursos de hardware existentes para realizar processamentos no menor tempo possível. O tempo de resposta ao usuário, no caso de acesso a telas de consulta e realização de operações, deve ser mantido mínimo de forma a garantir o uso responsivo. Tarefas longas devem ser realizadas em background, mas sempre tendo em vista o tempo e recursos computacionais.

Algoritmos eficientes para realização de processos são fundamentais para uma solução de alto desempenho, e cada requisito deve ser implementado tendo em mente a utilização de bons algoritmos. Todavia, de nada adianta um bom algoritmo sem uma arquitetura que provenha suporte adequado, o que levou às seguintes decisões arquiteturais e diretrizes:

  1. Regiões críticas mínimas - para manter alto desempenho com um grande número de usuários e acessos concorrentes é necessário manter controlado o número e tamanho de regiões críticas, que fazem necessário uso de locks. A redução das regiões críticas garantem baixa retenção mesmo com múltiplos acessos simultâneos, permitindo que os algoritmos executem com prejuízo mínimo mesmo em situações de carga elevada.
  2. Acesso racional ao banco de dados - os algoritmos devem ser construídos tendo em vista minimizar o número de acessos ao banco de dados e o volume de dados buscado. Sempre que possível, todos os dados a serem processados devem ser buscados em poucas consultas, dispensando acessos subsequentes.
  3. Caching - dados de uso frequente e de baixo nível de alteração devem ser armazenados em caches para evitar acessos desnecessários à sua origem e serem compartilhados entre threads de execução e diferentes requisições.
  4. Controle de granularidade - no casos de operações em lote, com o cargas de arquivos e batches de processamento, deve haver controle da segregação do lote em unidades menores. O processamento de unidades menores permite que o processamento seja paralelizado e distribuído de forma eficiente, além de evitar que transações fiquem grandes demais, o que pode aumentar a quantidade de memória necessária para o processamento, gerar retenção e crescimento do undo log no servidor de banco de dados. Por outro lado, unidades pequenas demais podem elevar o overhead do controle transacional e rastreabilidade, portanto deve ser feito um fine tunning para selecionar um tamanho de lote adequado.

Escalabilidade

A solução deve ser escalável, ou seja, capaz de atender a uma demanda crescente de uso através da ampliação da capacidade do hardware instalado. A escalabilidade deve ser tanto vertical, atingida pelo uso de servidores com mais recursos computacionais, quanto horizontal pela ampliação do número de máquinas no cluster.

A arquitetura define uso prioritário de dois canais de comunicação: HTTP e mensageria, para acessos síncronos e assíncronos, respectivamente. Ambas as formas de comunicação proporcionam paralelismo e distribuição das requisições, permitindo que a demanda seja distribuída em múltiplos servidores, cada um com múltiplas threads consumidoras, permitindo uso efetivo da capacidade instalada.

O acesso HTTP, seja pelo usuário através da interface Web ou via web services, deve ser direcionado a um load balancer, que direcionará a requisição a um dos nós do cluster. Cada nó do cluster também atua como consumidor de mensagens, vindas de um MoM tal como IBM MQ Series, distribuindo também o consumo desse tipo de requisição. Essa abordagem permite que a qualquer momento possa ser adicionado um novo nó ao cluster, ampliando a capacidade de processamento sem a necessidade de reconfiguração ou downtime.

A escalabilidade também depende da política de uso preferencial de locks otimistas e regiões críticas mínimas, permitindo que múltiplas threads de execução corram com retenção mínima por uso de recursos compartilhados, permitindo uma escalabilidade próxima de linear.

Alta disponibilidade

A solução deve permitir instalação em ambiente de alta disponibilidade e tolerante à falhas, de forma a minimizar o downtime e o prejuízo decorrente da indisponibilidade da solução. Sempre que possível o sistema deve ser recuperar de falhas e continuar operando de forma transparente ao usuário.

A tolerância a falhas é construída através do uso da instalação em clusters, que provêem redundância na disponibilidade da aplicação. Como cada nó opera de forma independente e o load balancer direciona requisições apenas aos nós disponíveis, a aplicação continua disponível mesmo que haja perda de um nó.

A falha de um nó é tornada transparente ao usuário através da replicação da sessão do usuário entre os nós, permitindo que as requisições web sejam atendidas por qualquer nó disponível no cluster.

Como toda operação é transacionada, inclusive com uso de transações distribuídas XA no caso de mensageria, a falha de um dos nós não coloca em risco a integridade dos dados da solução.

Segurança

A solução deve ser construída tendo como base preceitos que garantem a segurança da aplicação, tanto no que diz respeito invasão por agentes externos quanto prevenção de mal uso, seja intencional ou não.

Quanto à prevenção de agentes externos, é fortemente indicado que a aplicação se comunique exclusivamente de forma criptografada com o usuário. Se possível, a aplicação deve ser instalada em uma máquina protegida pelo firewall corporativo, totalmente isolada de acesso por agentes externos, permitindo acesso somente pela intranet e pela porta para acesso web. Na própria implementação da aplicação, deve se tomar os cuidados normais ao tratar input do usuário, evitando execução remota de código, SQL Injection e etc.

Quanto à prevenção de mal uso, diversas técnicas devem ser aplicadas, algumas delas fazendo parte da arquitetura e permeando toda a solução, impondo restrições aos usuários de acordo com seu perfil de segurança. A solução deve armazenar qualquer tipo de dado sobre os usuários da aplicação, delegando a autenticação e obtenção das credenciais de segurança ao servidor de LDAP corporativo.

Cada credencial de segurança está atrelada no sistema a um conjunto de permissões de acesso, que definem os limites do que o usuário consegue fazer. São definidos 2 tipos de permissões:

  1. Permissão de acesso à tela - permite que o usuário entre em uma tela específica. Cada tela está atrelada a uma permissão diferente, e telas que o usuário não possui permissão para acessar sequer devem ser visíveis no menu.
  2. Permissão de acesso à dados - a solução conta com filtro centralizado no modelo Chinese Wall, que segrega logicamente de forma transparente ao usuário dados de áreas separadas da instituição. Essa divisão permite que áreas isoladas, que não podem ter acesso aos dados umas das outras, utilizem a mesma instalação da solução sem que haja compartilhamento de dados confidenciais.

É ponto básico da estrutura de segurança também a capacidade de feito/conferido, no qual toda entrada de dados feita por um usuário deve ser autorizada por um segundo usuário antes de tomar efeito.

Também é um aspecto que deve permear toda a solução a rastreabilidade de tudo o que é feito no sistema. A trilha de auditoria é parte fundamental deste aspecto, contendo a origem de cada alteração cadastral e operação, incluindo nome do usuário, data, hora e IP da máquina que originou a requisição. Fora a trilha de auditoria, a rastreabilidade também se aplica a diversas outras funcionalidades, como memórias de cálculo, registros de realização de integrações e toda informação necessária para registrar o motivo e origem de cada alteração nos dados da solução.

Portabilidade

A aplicação deve ser portável, podendo ser implantada em uma variedade de ambientes distintos, suportando inclusive a implantação em instalações heterogêneas.

Para atingir a portabilidade necessária foi definido que o framework deve ser desenvolvida utilizando a plataforma Java Enterprise Edition, pois é portável a uma grande gama de arquiteturas de hardware e sistemas operacionais, além de ser estável, com bom desempenho e com conjunto de ferramentas e bibliotecas robustas para suportar uma aplicação de grande porte.

A aplicação será implantada em um container JEE, e deve utilizar apenas funcionalidades do container que seguem a especificação JEE, não ficando restrita a containers específicos ou versões específicas dos mesmos, dando portabilidade entre fornecedores e facilidade para atualização.

Para acesso ao banco de dados, a aplicação deve fazer uso da API JDBC, que em conjunto da ferramenta de mapeamento objeto-relacional Hibernate, que proporciona independência do banco de dados disponível no ambiente, e permite inclusive rápida adequação a novas versões.

A integração com mensageria deve ser feita através da API JMS, que também garante acesso padronizado e estável ao MoM. Em resumo, a utilização estrita da plataforma JEE, inclusive para integrações com outros serviços e sistemas legados, proporciona elevado nível de portabilidade e é plataforma de escolha para desenvolvimento da aplicação.