domingo, 31 de agosto de 2014

BeanMaker, Gerador de Código - Arquitetura

Aprendi com o professor Carlos Ribeiro o desenvolvimento em camadas em Java. Ele nos mostrou como implementar um DAO Genérico usando JPA e JDBC. Trago essas lições até hoje.

Fiz uma pequena modificação no modelo ensinado com maestria por ele, inserindo a camada "Transaction". Ela tem por objetivo maximizar o reuso dos serviços existentes.

Um serviço é uma peça de código que executa uma regra de negócio dentro de uma transação de Banco de Dados. Uma transação de Banco de Dados garante que: (a) Ou toda a regra de negócio é executada (b) Ou nenhuma parte da regra de negócio é executada. Por exemplo: Imagine uma transação do setor bancário por ser muito bem conhecida de todos nós: o Saque. Ela é composta por duas ações básicas: (1) Retirada do Valor da Conta e (2) Gravação do Lançamento no Extrato. Teria mais ou menos a seguinte ordem:
  (Serviço de Saque)
  1) Início da transação do Banco de Dados
  2) Tratamento do débito
  3) Gravação do Extrato
  4) Fim da Transação bem sucedida, com efetivação no Banco de Dados (commit)
  5) Fim da Transação mal sucedida, desfazendo as alterações no Banco de Dados (rollback)

Da mesma forma, uma transação de Depósito teria a seguinte estrutura:
  (Serviço de Depósito)
  1) Início da transação do Banco de Dados
  2) Tratamento do crédito
  3) Gravação do Extrato
  4) Fim da Transação bem sucedida, com efetivação no Banco de Dados (commit)
  5) Fim da Transação mal sucedida, desfazendo as alterações no Banco de Dados (rollback)

Essas transações na vida real são mais complexas do que isso. Um Saque somente pode ser realizado se houver saldo em conta, por exemplo.

Trabalhando com a arquitetura sem a nova camada "Transaction", senti dificuldades para construir "serviços" a partir de outros "serviços", uma vez que ele já englobava o controle de transações do Banco de Dados. Imagine que eu queira fazer uma regra de negócio para a "Transferência entre Constas", que é composta de "Saque na conta de origem" e "Depósito na conta de destino". Gostaria de ter um novo trecho de código que tivesse a seguinte estrutura:
  (Serviço de Transferência Entre Contas)
  1) Início da transação do Banco de Dados
  2) Executar Serviço de Saque
  3) Executar Serviço de Depósito
  4) Fim da Transação bem sucedida, com efetivação no Banco de Dados (commit)
  5) Fim da Transação mal sucedida, desfazendo as alterações no Banco de Dados (rollback)

Observe que, como tanto o Serviço de Saque como o Serviço de Depósito já possuem o controle de transação, não conseguiria que se ocorresse algum erro na transação de "Depósito" a transação de saque fosse desfeita.

Na verdade, quando chamamos uma transação dentro de uma outra transação é esperado que haja um erro de programação.

Para resolver esse problema é que a camada "Transaction" foi criada. Ela sempre conterá um trecho de código muito simples, como o modelo abaixo:
  (Transação Hipotética)
  1) Início da transação do Banco de Dados
  2) Executar Serviço Hipotético
  3) Fim da Transação bem sucedida, com efetivação no Banco de Dados (commit)
  4) Fim da Transação mal sucedida, desfazendo as alterações no Banco de Dados (rollback)

Trazendo as regras de negócio para a nossa arquitetura com mais uma camada, teremos:
  (Serviço de Saque)
  1) Tratamento do débito
  2) Gravação do Extrato
  (Serviço de Depósito)
  1) Tratamento do crédito
  2) Gravação do Extrato
  (Serviço de Transferência Entre Contas)
  1) Executar Serviço de Saque
  2) Executar Serviço de Depósito

O que nos obrigaria ter as seguintes Transações:
  (Transação de Saque)
  1) Início da transação do Banco de Dados
  2) Executar Serviço de Saque
  3) Fim da Transação bem sucedida, com efetivação no Banco de Dados (commit)
  4) Fim da Transação mal sucedida, desfazendo as alterações no Banco de Dados (rollback)
  (Transação de Depósito)
  1) Início da transação do Banco de Dados
  2) Executar Serviço de Depósito
  3) Fim da Transação bem sucedida, com efetivação no Banco de Dados (commit)
  4) Fim da Transação mal sucedida, desfazendo as alterações no Banco de Dados (rollback)
  (Transação de Transferência Entre Contas)
  1) Início da transação do Banco de Dados
  2) Executar Serviço de Transferência Entre Contas
  3) Fim da Transação bem sucedida, com efetivação no Banco de Dados (commit)
  4) Fim da Transação mal sucedida, desfazendo as alterações no Banco de Dados (rollback)

Assentados esses conceitos, podemos prosseguir na demonstração do BeanMaker.

sábado, 30 de agosto de 2014

BeanMaker, Conceituação – Construindo código para a primeira classe

Para a demonstração do BeanMaker, utilizarei o modelo de dados abaixo, para um sistema hipotético de gestão de pessoas em projetos. É um modelo simples, mas que possui as relações mais importantes entre tabelas (classes). Lembre-se que o resultado pretendido é gerar todo o código para a manutenção das tabelas a partir das classes da camada “modelo”, incluindo apenas anotações que servirão como diretivas para o processo de geração.

Preferi nesse momento apresentar um modelo de dados com notação de “Entidade - Relacionamento”. Não usei o diagrama de classes, apenas por que o MER já estava disponível.

Iniciando pelo cenário mais simples, vou a explorar a construção de uma das classes mais simples do nosso modelo de dados proposto. Para simplificar ainda mais, vou considerar que essa classe não tem qualquer relacionamento com as demais classes.

A classe pretendida é a “Diretoria” que tem apenas duas propriedades, o “id” que servirá de chave primária e o seu “nome”. É de se supor que o “nome da diretoria” seja único. Por hora, não vou levar em conta o seu relacionamento com departamento (uma diretoria pode ter muitos departamentos). Pretendo explorar essa característica na próxima postagem.

Olhando para essa classe, podemos considerar que as principais funcionalidades para ela são: CRUD (insert, select, update e delete), a recuperação de um exemplar (instância) a partir do seu ID, a recuperação de um exemplar (instância) a partir do seu nome (ele é único) e a recuperação de uma lista de diretorias a partir de um texto que contenha uma parte do nome.

Na próxima postagem, quando incluir o seu relacionamento com “Departamento” veremos que outras funcionalidades serão pretendidas.

Baseado no cenário super reduzido acima, poderemos por um caminho mais plano e facilitado na nossa primeira geração de código.

Podemos então considerar que o código da nossa classe Diretoria é o seguinte:

public class Diretoria {
   private Integer idDiretoria;
   private String nmDiretoria;
   // constructors
   // getters and setters
  // methods like “toString”
}

Vou agora introduzir as anotações da JPA que irão nos apoiar em toda a geração de código. Observe que elas funcionarão apenas como “diretivas” para a construção do código, uma vez que construiremos o acesso aos dados via JDBC. Não é meu objetivo falar sobre a JPA e suas anotações. Parto do princípio que você já as conhece. Vou tecer comentários apenas quando elas forem relevantes para o meu propósito.

A nossa classe passou a ter o seguinte código:

@Entity
@Table (name=”Diretoria”)
public class Diretoria {
   @Id
   @Column (name=”idDiretoria”)
   private Integer idDiretoria;
   @NotNull
   private String nmDiretoria;
   // constructors
   // getters and setters
  // methods like “toString”
}

A nossa especificação de classe já tem muitas informações (anotações) para geração do código de acesso aos dados e uma (@NotNull) que também serve para a construção de uma tela (view). Contudo, faltam outras que são cruciais para a construção do nosso código. Como não existem anotações para todos os objetivos que preciso, criei alguns pacotes com anotações proprietárias para o BeanMaker. Todos as nossas anotações proprietárias serão prefixadas por “BM” (de BeanMaker). A medida que elas forem surgindo apresentarei a motivação para a sua criação.

Quase passei batido, mas é importante perceber que a anotação “@NotNull” é uma importante diretiva para a construção da tela. Podemos através dela definir que essa propriedade será mapeada para um campo de tela obrigatório (required, em JSF). Outra importante informação para a construção da tela é derivada da anotação “@Id”, que vai estabelecer que essa propriedade não pode ser alterada pelo usuário, então será gerado um campo apenas de leitura.

A versão final da nossa classe passou a ter o seguinte código:

@Entity
@BMMenu(option=”Cadastro” position=1)
@BMI18NClass(value=”Diretoria”)
@Table (name=”Diretoria”)
public class Diretoria {
   @Id
   @Column (name=”idDiretoria”)
   @BMViewField(size=5, maxLength=5)
   @BMI18NProperty(value=”Id”)
   private Integer idDiretoria;
   @NotNull
   @BMViewField(size=50, maxLength=50)
   @BMI18NProperty(value=”Nome”)
   @BMDataPropertyUniqueKey
   @BMDataListBy
   private String nmDiretoria;
   // constructors
   // getters and setters
  // methods like “toString”
}

Foram introduzidas as seguintes anotações proprietárias:
a) A anotação “@BMMenu” determina através do elemento “option” em qual menu será invocada a chamada da tela (view) para a manutenção da classe. O elemento “position” define em qual posição dentro da opção ela será inserida.
b) A anotação “@BMViewField” define o comportamento da tela. Observe que ela possui informações (elementos) para definir o tamanho do campo para o campo da tela que é ligado à propriedade. Essa anotação tem alguns outros elementos que não foram apresentados agora, como por exemplo o “component” que defineá o componente que é utilizado na construção da tela (poderíamos querer que fosse uma “h:inputTextarea” ao invés de um “h:inputText”).
c) A anotação “@BMDataListBy” define que deverá ser gerado um método que utilizará essa propriedade para buscar um conjunto de exemplares (instâncias) no BD que atendam ao critério de pesquisa.
d) A anotação “@BMDataPropertyUniqueKey” define que essa propriedade é mapeada no banco de dados com um índice único. Assim, ela permite que o gerador de código crie métodos para obtenção de um exemplar (instância) do objeto no banco de dados através dessa propriedade. Também inclui a validação de duplicidade nos métodos de inclusão (insert) e atualização (update).
e) A anotação “@BMI18NClass” cria uma entrada no “resourceBundle” para a internacionalização da nossa tela. Ela associará o valor explicitado (em “value”) à chave criada automaticamente para a classe.
f) Semelhantemente a anotação “@BMI18NProperty” cria uma entrada no “resourceBundle” para a internacionalização da nossa tela. Ela associará o valor explicitado (em “value”) à chave criada automaticamente para a propriedade.

Para ilustrar, apresento um modelo parcial de uma tela (view) com as respectivas ligações com as anotações declaradas acima.

De posse das anotações da JPA e das anotações do pacote BMData é possível gerar o código das diversas camadas da arquitetura do no projeto. Assim, apenas para exemplificar, vou apresentar o código gerado para a interface da camada de acesso aos dados.

public interface DiretoriaDAO {

 Diretoria insert(Diretoria o) throws DataAccessException;

 boolean update(Diretoria o) throws DataAccessException;

 boolean delete(Diretoria o) throws DataAccessException;

 //Método gerado a partir da anotação “@BMDataListBy”
 ArrayList listByNmDiretoria(String nmDiretoria)
         throws DataAccessException;

 //Método gerado a partir da anotação “@BMDataPropertyUniqueKey”
 Diretoria getByUniqueKeyNmDiretoria(String nmDiretoria) 
         throws DataAccessException, ObjectNotFoundException;

 //Método gerado a partir da anotação “@Id”
 Diretoria getById(Integer id) 
         throws DataAccessException, ObjectNotFoundException;

 //Método gerado a partir da anotação “@Id”
 Diretoria getByIdLock(Integer id) 
         throws DataAccessException, ObjectNotFoundException;
}

Neste artigo apresentei como o BeanMaker utiliza as anotações da JPA para gerar o código de acesso aos dados e para a montagem da tela. Foi uma quantidade considerável de conceitos para gera uma única e simples classe que não possuía nenhum relacionamento.

Em um próximo artigo apresentarei como o BeanMaker se comporta em classes que tenha um relacionamento dos tipos “Um Para Muitos” e “Muitos Para Um”.

sexta-feira, 29 de agosto de 2014

BeanMaker, Conceituação – Plataforma de Desenvolvimento

O desenvolvimento de um sistema tem uma etapa crucial: a escolha da tecnologia que será utilizada.

No BeanMaker estou utilizando a seguinte plataforma tecnológica: Java 8, JSF 2, RichFaces 4.3, Tomcat 8, Oracle 11XE, MySql 5.6 e Eclipse Luna. Pretendo gerar também versão para PostGres e Sql Server.

Espero ansiosamente a versão 4.5.0 do Richfaces que terá suporte para o JSF 2.2.

Uso também o CA-ERWIN Comunity Edition para projetar os modelos de Dados e o Visual Paradigm Comunity Edition para gerar os artefatos UML.

A minha principal preocupação foi escolher uma plataforma que tivesse versão free, mas com a possibilidade de suporte comercial. A opção de qual usar é do cliente usuário.

Por fim, estou usando a camada de acesso com DAO por não me sentir confortável (experiência) com os Frameworks de ORM.

Também foi fundamental para minha decisão o meu conhecimento da plataforma Java.

BeanMaker, Conceituação – Um passo atrás

Antes de avançarmos no detalhamento do projeto BeanMaker, gostaria de dar um passo atrás para falar de alguns conceitos que estão governando a sua construção. Os conceitos e técnicas aqui apresentados são de domínio público e são altamente inter-relacionados. Você irá perceber isso, pois alguns são muito parecidos e, frequentemente, faço referência de um conceito dentro de uma característica técnica e vice versa.

Um outro aspecto importante que quero destacar é a motivação. Nesses quase trinta anos de experiência profissional, muitas vezes me peguei perguntando se eu já não havia desenvolvido algo semelhante antes, que eu pudesse usar como uma escada para o desenvolvimento da nova funcionalidade. Ou perguntando, será que não dá para automatizar esse processo de desenvolvimento. Já escrevi diversas pequenas ferramentas (provas de conceito) para me auxiliar em atividades repetitivas. Todas elas foram bem sucedidas.

Obviamente, tenho consciência que ainda não é possível desenvolver toda uma aplicação usando esses conceitos. Mas acredito firmemente (e pretendo demonstrar através desse projeto) que quase todas as funcionalidades (mais de 80%) para as interações homem-máquina possam ser automatizadas a partir do modelo de dados e da introdução de algumas diretivas (anotações) nas implementações das suas classes. Por interação homem-máquina, chamo as telas para captação e exibição dos dados, a mediação dessas telas com as regras de negócio e a sua correta gravação em mecanismos de armazenamento de dados.

Observe que, embora não esteja explicito no parágrafo anterior, as regras de negócio ainda não podem ser automaticamente construídas. Elas podem ser esquematizadas, mas o seu “recheio” tem que ser construído por um técnico. Assim, uma regra de negócio que envolva, por exemplo, o cálculo dos juros de uma operação de crédito, pode ter a captação e a gravação dos seus dados automatizados, mas o cálculo e as suas interações com outras regras de negócio terão que ser escritas “no braço”. Para escrever regras de negócio que possam ser automatizadas, seria necessário um formalismo na definição, através de fórmulas matemáticas e de um tradutor para transformar essas fórmulas matemáticas em código (em programas).

Em engenharia de software existem alguns conceitos que são tão importantes que até viraram siglas. Alguns são complementares. Estou aplicando uma série deles no projeto, dentre eles:

DRY – Don’t repeat yourself (Não se repita). É o conceito por trás da reutilização. Também é um conceito matemático (evidenciação). Se existe algum trecho de código que vai ser muito utilizado, separe-o para que você possa utilizá-lo sempre que necessário.

KISS – Keep It Simple Stupid! (Tolo, mantenha isso simples!). Não invente moda. Não complique. Não introduza variáveis desnecessárias no momento. Não tente prever o futuro.

CoC – Convention Over Configuration. Sempre que possível adote convenções que possam ser consideradas válidas a menos que tenham sido explicitadas (configuradas) quando necessário. As convenções são características que responderão por mais de 90% dos casos, assim evitamos ter que descrever situações que são a regra e não a exceção. Existem também padrões de construção que tem quase a unanimidade da comunidade acadêmica e técnica. Estou usando alguns deles para organizar as ideias e o código. Forma um conjunto de boas práticas que facilitam a construção de software ao permitir que o esforço de desenvolvimento possa ser distribuído entre os membros da equipe e até mesmo que sejam “atacados” por profissionais com perfis técnicos diferentes.

Observe que esses padrões são mais “físicos” que os conceitos definidos anteriormente Alguns dos conceitos que defini como norteadores no desenvolvimento do projeto são:

Multi-Camada:
O desenvolvimento em várias camadas preconiza que o código seja dividido em um conjunto de fatias, onde uma camada comunique-se com as camadas que lhe são adjacentes. Não pode haver um salto entre camadas. Por exemplo: A camada que controla a exibição da tela, não pode ter acesso direto à base de dados. Ela tem que (obriga-se à) chamar a camada de regras de negócio, que por sua vez chama a camada de acesso aos dados, se necessário.

Assim, se algum técnico viola essa regra, a violação é facilmente percebida e pode ser rapidamente corrigida.

Principais benefícios: Imagine os seguintes cenários:
a) Você precisa mudar o dispositivo que apresenta as telas do seu sistema, ou ainda, que você precisa exibir essas telas em dois dispositivos. Usando esse padrão de desenvolvimento, você precisa apenas alterar ou construir a camada de apresentação.
b) Você precisa mudar o banco de dados da sua aplicação. Você irá alterar apenas a camada de acesso aos dados.
c) Você precisa alterar uma regra de negócio. Você irá alterar apenas a camada de regra de negócios. Obviamente, se essa alteração implicar na captação de novos dados nas telas envolvidas ou no armazenamento e recuperação de novos dados, você terá que alterar outras camadas.

OOA (Object Oriented Analysis)
A Análise Orientada à Objetos é hoje a principal técnica de desenvolvimento de sistemas. Embora existam muitos críticos ao seu emprego, quase todas as linguagens de programação modernas a implementam e quase todos os profissionais na área de sistemas a conhecem. Ou Creio que não exista no Brasil curso superior em sistemas que não a apresente em seus currículos. Então é quase uma ditadura, ou você usa fora do mercado.

MVC (Model View Controller):
É um padrão de projeto (alguns teóricos dirão que é uma arquitetura de projeto) que preconiza que existam três camadas distintas: A camada de Visão (view), a camada de Dados (Model) e a camada de Controle (Controller). A camada de visão é composta pelas telas, relatórios ou outros mecanismos de apresentação ou captação dos dados no ambiente externo à aplicação. A camada de Dados (Model) é composta pelos dados, funções e regras de negócio. A camada de Controle (Controller) é a responsável por fazer a mediação entre as outras duas. Então ela recebe a solicitação da tela (View), executa a regra de negócios (Model), recupera dados na base de dados (Model) e os encaminha para algum dispositivo (View) para a sua apresentação.

DAO (Data Access Object):
É um padrão de projeto que permite o isolamento do acesso aos dados, permitindo que os componentes de armazenagem sejam trocados sem causar impacto nas demais camadas do projeto.

No próximo artigo descreverei a arquitetura da solução e mais alguns insights de como pretendo atacar alguns dos problemas que se apresentam no projeto.

quinta-feira, 28 de agosto de 2014

BeanMaker, Gerador de Código - Conceituação

Durante os últimos meses vinha amadurecendo a ideia de desenvolver uma ferramenta de geração de código que pudesse acelerar o desenvolvimento dos projetos em que estou envolvido. Resolvi, então, investir tempo na sua construção, com a firme esperança de recuperá-lo no projeto das minhas outras aplicações.

Esse tipo de ferramenta em que estou trabalhando é comumente chamado de "scaffolding". Existem vários para várias tecnologias. Só que o código gerado delas não me agradaram. Fora isso encontrei dificuldades para adaptá-las ao meu jeito de trabalhar.

Já que estava iniciando o projeto, optei por trabalhar com as tecnologias mais modernas em Java.

  • Java 8
  • Apache TomCat 8
  • JSF 2
  • JBOSS RichFaces 4.3.7
  • Eclipse Luna

Incluí os seguintes requisitos para o código gerado. Futuramente postarei justificativas para esses requisitos. São eles:

  • Multi Língua
  • Multi Database
  • Multi Browser
  • Horas no formato GMT

Idealizei o projeto do modelo de Dados (MER e diagrama de classes). Executei também uma prova de conceito que foi muito bem sucedida.

A premissa básica do projeto é que o código possa ser completamente gerado a partir do diagrama de classes, ou mais especificamente das propriedades de uma classe. Assim, dada uma classe hipotética "X", com duas propriedades "p1" e "p2", podemos gerar código (novas classes) completamente funcional para acessar o banco de dados (CRUD) para acessar colunas "p1" e "p2" da tabela "X", controle de transação, tela (view) e relatório básicos. O relacionamento entre as classes do modelo também permite tomar algumas decisões quanto a montagem da tela (view).

Para isso, estou usando anotações (annotations) de pacotes existentes e criando algumas anotações do projeto. Essas anotações governam o processo de criação do código. O assunto "anotações" merecerá uma postagem exclusiva em breve.

Exemplo do uso de anotações já existentes na JPA2 e seus impactos no código:

  • @NotNull: permite que a propriedade seja considerada obrigatória.
  • @NaturalId: permite que seja criada uma restrição de integridade de unicidade.
  • @Id: permite montar cláusulas de seleção bem definidas

Exemplo do uso de anotações criadas para o projeto:

  • @ViewStrategy: define como os relacionamentos (OneToMany, ManyToOne) serão representados na tela.
  • @ViewFieldSize(size=x maxLength=y): permite a definição do tamanho dos campos na tela.

Pretendo ter a primeira versão do projeto pronta já na próxima sexta-feira (5/9).

É minha intenção que o projeto seja comunitário e de código aberto.