SyntaxHighlighter

Mostrando postagens com marcador C#. Mostrar todas as postagens
Mostrando postagens com marcador C#. Mostrar todas as postagens

segunda-feira, 4 de agosto de 2014

Integração Contínua em .NET – Parte I

Automatizando seus Processos de Construção, Compilação, Testes, Documentação e Implementação de seus Sistemas em .NET

Nesta primeira parte de nossa série sobre Integração Contínua – mais conhecida na comunidade como CI, o leitor aprenderá sobre o aspecto técnico e psicológico em implementar o conceito de Integração Continua e as vantagens e desvantagens de implementá-la nas organizações vencendo as objeções que aparecem durante o processo até a sua adoção em definitivo.
Nesta Série, nós também introduziremos o leitor com o uso muitas ferramentas gratuitas e de baixíssimo custo tais como Subversion, MSBuild, NAnt, Jenkins e CruiseControl.NET que são partes de um processo completo de Integração Contínua e demonstraremos em detalhes como usar estas ferramentas,neste artigo demonstraremos um processo de Integração Contínua simples. Vamos criar um pequeno Sistema de aprovação de execução de builds que nos dará um maior controle para que nossas alterações sejam publicadas em ambientes estáveis de desenvolvimento, Homologação e Produção. Criaremos um Estudo de Caso que será levado à efeito com o uso da Integração Contínua ao longo desta série. Contaremos o caso da Empresa de Software Fictícia Imbróglio de Códigos LTDA e como a mesma conseguiu se salvar da falência total implementando lentamente o que chamamos de Os 7 Ciclos do Sucesso para a Integração Contínua.

Como desenvolvedores,  estamos interessados em desenvolver nossas aplicações da melhor forma possível para os nossos clientes como um minimo de esforço. Mas, nos tempos atuais, com aplicações mais complexas e tendo cada vez mais "partes móveis", criar grandes aplicações está se tornando cada vez mais difícil, mesmo com o avanço nas ferramentas de desenvolvimento, tais como o Visual Studio e .NET Framework. Uma das chaves para a melhoria da produtividade,e consequentemente das aplicações entregues, é a automatização da parte  repetitiva do trabalho e a Integração Contínua é uma das melhores formas de se fazê-lo.
Não podemos crer que o Processo de Integração Contínua seja a bala de prata ou a solução para todos os males, mas onde ela tem sido implementada, tem tido grande sucesso e melhorias no processo de software para as equipes de desenvolvimento.
A Integração Contínua, também conhecida como CI - Continuous Integration - é o ponto central do desenvolvimento de software atualmente. De fato, quando a CI é introduzida em uma empresa, torna-se o grande divisor de águas, ela altera radicalmente a forma como as equipes pensam sobre todo o processo de desenvolvimento. A CI tem o potencial de permitir e executar uma série de melhorias contínuas e incrementais nos processos, indo de um processo de simples agendamento de construção - vamos usar o termo build – até o processo de Entrega Contínua que culmina no processo de publicação nos demais ambientes para Teste, Homologação ou Produção.
Uma boa infraestrutura que possibilite a Integração Contínua pode simplificar o processo de desenvolvimento até a entrega do software ou de sua iteração, auxiliando a identificação e correção de  falhas rapidamente. Também facilita a disponibilização de uma dashboard muito útil no Projeto tanto para desenvolvedores quanto para outros profissionais da empresa que não são desenvolvedores e, ultimamente, ajuda equipes a agregar mais valores ao negócio e ao produto sendo desenvolvido para o cliente. Todo time de desenvolvimento, não importando se grande ou pequeno, deve implementar e pensar como Integração Contínua.

O que é a Integração Contínua?

Voltando aos dias dos Projetos desenvolvidos utilizando a Metodologia em Cascata e controlando os cronogramas com gráficos de Gantt, bem antes da introdução das práticas de Integração Contínua, o tempo e a energia da equipe de desenvolvimento era drenada diretamente para um procedimento chamado de Fase de Integração, que era executado sempre um passo anterior à entrega do software. Durante esta Fase, a alteração do código era feita por alguns desenvolvedores ou pequenos times que eram reunidos de forma isolada e se juntavam apenas na integração de todo o trabalho já feito. Era um trabalho complicado, na maioria das vezes durava meses, em uma integração com mudanças de código conflitando entre si. Era muito dificil antecipar quais tipos de problemas iriam ocorrer durante este processo e era ainda mais oneroso corrigir os erros provenientes de tais integrações pois envolviam quase sempre retrabalho e reengenharia de código criado meses ou semanas antes. Este processo complicado, oneroso e doloroso, cheio de riscos e perigos, frequentemente causava um grande atraso na entrega, custos não planejados e, como resultado de tudo isso: clientes insatisfeitos. A Integração Contínua nasceu para resolver estes problemas e sanar estas dificuldades. Na sua forma mais simples, a CI envolve ferramentas que monitoram seus Sistema de Controle de Versão (Box 1) buscando sempre alterações. Logo que uma alteração no código é identificada, O Servidor automaticamente compila e testa o código de sua aplicação. Se algo dá errado, a ferramenta imediatamente notifica os desenvolvedores para que eles possam corrigir o problema imediatamente. Mas, a Integração Contínua pode fazer muito mais do que isso.

Sistema de Controle de Versão (SCM).
É um software que tem como principal funcionalidade, controlar versões de um documento qualquer,ou seja, cada alteração que é feita neste documento, se torna uma versão deste e o SCM controla de forma sequencial tais versões. Tal software é muito utilizado em ambientes de desenvolvimento seja ele corporativo ou não.

A Integração Contínua pode também ajudar a equipe a manter a saúde do seu código, monitorar automaticamente a qualidade de código e as métricas em seu código e desta forma auxiliar a manter a Dívida Técnica (Box 2) controlada e os custos de manutenção baixos.

Dívida Técnica.
Dívida Técnica descreve a dívida que a equipe de desenvolvimento assume quando escolhe um design ou abordagem fácil de implementar no curto prazo mas com grande impacto negativo no longo prazo.
A visibilidade pública das métricas de qualidade de código encoraja os desenvolvedores a terem um maior cuidado quanto à qualidade de seu código e melhorá-lo, devemos lembrar que o principal fundamento da Integração Contínua não é punir, mas sim facilitar a manutenção do código afinal de contas o código é de todos. Combinando testes de aceitação, a CI pode também funcionar como uma ferramenta de comunicação, mostrando um verdadeiro Raio-X do estado atual do Projeto em andamento dentro e fora da equipe. Pode simplificar e acelerar a entrega auxiliando a automação do processo, deixando a publicação da última versão da aplicação totalmente automática ou permitindo a operação com apenas um aperto de botão. Em sua essência, a Integração Contínua age sempre na redução de riscos à aplicação e seu desenvolvimento nos dando uma resposta rápida.
Desta idéia, tiramos então uma conclusão lógica: precisamos sempre enviar qualquer alteração de nosso código para teste antes de entrar em produção. De certa forma, o uso da Entrega Contínua, está intrinsecamente ligado a Integração Contínua, posto que esta termina e começa aquela que nada mais é do que automatizar uma implementação do website, devolver um módulo pronto ou outra atividade de fim pois a Integração Contínua é atividade de meio. Então, uma abordagem pura da Integração Continua pode não ser apropriada para todas as equipes. Um exemplo disto, é que muitos usuários não vêem com bons olhos novas versões de software sendo entregues muitas vezes em uma semana e com isto as solicitações para que os mesmos façam seus testes de aceitação e acabam por preferir um ciclo mais espaçado e com um prazo no qual o mesmo possa efetuar tais Testes.
O processo de Integração Continua, e em particular Publicação Continua - Continuous Deploy - e Entrega Contínua - Continuous Delivery, são muito mais do que entregar produtos para os usuários de forma rápida. Quanto tempo seu time faz pequenas alterações de códigos antes de ir para produção? Quanto custa o processo de resolver problemas que ocorreram há muito tempo gerando grandes demandas de falha? Para que o CI possa resolver todos os nossos problemas, devemos não só programar como Integração Contínua como também devemos pensar como Integração Contínua.
Corrigir builds quebrados se torna prioridade absoluta e não devem deixar estagnar o processo. O processo de implementação precisa ser automatizado, com nenhum passo manual envolvido.
Uma das definições mais conhecidas de Integração Contínua foi dita por Martin Fowler que diz: “Integração Continua é uma prática dentro de desenvolvimento de software onde membros de um time integram seu trabalho freqüentemente, normalmente cada um executa no mínimo uma a várias integrações por dia. Cada integração é verificada por um build automatizado (incluindo os testes) para identificar erros de integração o mais rápido possível. Muitas equipes vêem nesta forma de trabalho uma redução significativa nos problemas de integração e permitem uma equpe desenvolver software de uma forma coesa mais rapidamente.”
Resumindo, um processo automatizado que constrói, testa, analisa e implementa uma aplicação para ajudar e garantir que o processo funcione corretamente seguindo as melhores práticas e as tornem entregáveis. Este processo é executado em cada mudança de código fonte e permite resposta imediata para a equipe de desenvolvimento. Diante disto, temos um termo que precisamos compreender que é o Build. O Build, nada mais é do que o processo de compilar, analisar e entregar o assembly pronto para implementação. Exemplificando, pode ser que, desenvolvamos uma camada da aplicação e commitemos seu código dentro do Controle de Versão, logo, o Sistema de Integração Continua, quando for executar a sua tarefa rotineira, irá compilar, testar e posteriormente gerar o assembly no local já pronto.

Processos de Desenvolvimento para Integração Contínua

Seu processo de desenvolvimento é ágil? Em sua equipe é utilizada Extremme Programming - XP -, Scrum ou algum outro? Sua companhia é profundamente enraizada em usar o método Cascata? Seu processo está em algum lugar entre Ágil e Cascata? A resposta a estas perguntas não importa, porque todas as equipes sem exceção devem seguir um processo parecido com o que é mostrado a seguir.

1.      Baixa o código fonte, necessário para a programação, de seu SCM.
2.      Faz alterações neste código.
3.       No Visual Studio, clica em Build e espera que o código compile com sucesso.
4.       Volta ao passo dois, caso o Visual Studio identifique erros.
5.       Executa os testes unitários, e espera que tudo fique verde, ou seja, que passe em todos.
6.       Volta ao passo dois, caso o teste unitário falhe, neste caso, o desenvolvedor vê um alerta vermelho e pode ver mais de um.
7.       Refatora o código tornando-o mais compreensível e então volta ao passo cinco.
8.      Efetua o envio do seu código alterado para o SCM, este trabalho é chamado de commit.

Vendo o processo demonstrado no parágrafo anterior e quando começamos a utilizar o CI, notamos que seguimos o mesmo processo. Porém, para o CI, o processo ainda vai mais além, posto que ele faça todos estes passos e ainda continua após o commit do código por parte do desenvolvedor, como veremos a continuação dos passos mais adiante.

9.      O Servidor de CI observa as alterações no SCM.Quando encontra qualquer nova alteração, ele efetua um download da última versão do código contendo o último commit.
10.   O Servidor de CI efetua o build do código.
11.  O Servidor de CI executa o teste unitário.
12.   O Servidor de CI envia os resultados do builds e o resultado dos testes para um sistema de comunicação, normalmente e-mail, mas também pode ser em SMS, Mensageiro instantâneo ou outro, para informar aos membros do time o estado do build.

Tipos de Construção para a Integração Contínua

Os passos da Integração Contínua que descrevemos anteriormente nos trazem o entendimento de  que cada vez que um desenvolvedor altera e sobe o código, um build é acionado, porém, em verdade, este é o último objetivo e a razão pela qual este processo é chamado de Integração Contínua.
Uma forma de começar a configurar o seu Servidor de CI é obter as últimas alterações de seu código e efetuar o build.
No próximo passo, adicione os testes unitários. E então, execute esta operação diariamente, que chamaremos de Build diário.
Mas, veremos que, em um determinado momento, um build diário inclui outras coisas que não nos atendem quando fazemos um build incremental. Quando temos builds executados todos os dias, podemos adicionar a este processo mais dois ou três builds que apenas constroem e testam o software. Não demorará e estaremos construindo e adicionando compilações diferentes para fazer coisas diferentes o tempo todo. Existem tipos de builds que dependem do ambiente e da natureza das aplicações, alguns dos builds mais comuns são mostrados na tabela 1.

Tipo de Build
Descrição
Contínuo ou Incremental
É executado quando o código é commitado no SCM. Executa uma compilação rápida e um ou outro Teste Unitário.
Diário ou Noturno (Daily ou Nightly Builds)
Executa uma compilação e um gama maior de testes unitários e em alguns casos pode executar testes adicionais como testes de aceitação e teste de Interface de usuário.
Semanal
Executa a compilação, testes de unidade completos e testes adicionais como testes de aceitação, testes de fumaça e afins.
QA
Cria um build apenas para verificações do pessoal do Setor de Qualidade contendo as métricas para tal.
Staging ou Pré-Produção
Consiste em ser um servidor que age como uma réplica perfeita do ambiente de produção, também pode ser chamado de pré-produção em alguns lugares. Nesta categoria, o build é executado e retornando sucesso, deixa os assemblies disponíveis para testes em pré-produção.
Release
Cria um conjunto de instalação e então efetua os testes de instalação e todos os outros processos necessários ao funcionamento do sistema em Produção e retorna o feedback.
Tabela 1. Tipos de Build

O build mais importante, e é justamente o que nos dá o resultado mais rápido, é o build contínuo ou incremental. Tal build é automaticamente chamado quando o CI identifica uma alteração no código fonte commitado no SCM. Como este build é executado muitas vezes durante o dia e um build pode ser executado logo após o término de outro. Este pode ser executado a cada cinco ou dez minutos.
Ocupando o segundo lugar em frequência, por ser executado em um período mais espaçado, é o build diário, também chamado de build noturno. Tal antítese pode ter gerado alguma confusão, posto que a pergunta que se faz é: ele é noturno ou diário? A resposta é: não importa, posto que ele seja executado uma vez por dia e preferencialmente quando não se tem quase nenhuma atividade na infra-estrutura, pode ser um horário durante o dia em que todos devem ter ido almoçar ou durante a madrugada, por isso a dualidade, que pode ser conflitante em alguns casos, de conceitos. Tal tipo de build é agendado para que execute todo um gama de testes unitários em todo o código, sem exceção, por isso que é muito importante ter um seu Projeto uma estratégia de testes para os módulos bem definida para que estes builds nos devolvam a informação verossímil e necessária, também verificamos a qualidade do software utilizando Cobertura de Códigos e Métricas de software.
Outro tipo de build é o Build Semanal, que executa automaticamente e de forma usual sempre aos finais de semana. Uma vez por semana é o suficientes para medirmos a qualidade de nosso código e executar Testes adicionais com ferramentas para testes de fumaça como Selenium, FitNesse e NUnitForms. Também, podemos criar a documentação de nosso código utilizando SandCastle e fazer a Integração Contínua de nossa base de dados.
Para um controle maior da data de liberação das novas versões do seu sistema, além do build semanal, também seria recomendado um build de release.
O propósito do build do release é criar um conjunto de instalação e executá-lo. Este build normalmente é executado manualmente. Mas, após o build ser iniciado, todos os outros passos são executados automaticamente. No build de release, o CI compila todo o código Fonte, executa todos os testes criados, incrementa o número de versão e etc. Então é criado o conjunto de artefatos para a implementação do mesmo e posterior teste desta implementação. Podem existir outros tipos de builds de acordo com as necessidades.
  
CI em alguns passos para a sua organização

A Integração Contínua não é a solução para todos os nossos problemas como já foi dito anteriormente. Claro que introduzir a CI dentro da empresa nos mostra um caminho a percorrer através de ciclos distintos. Cada um destes ciclos envolve melhorias que são incrementais entre si para todo o corpo técnico, sejam eles servidores ou profissionais da equipe de desenvolvimento bem como melhorias na cultura e estrutura de trabalho. Vamos mostrar nos parágrafos seguintes, um pouco de cada ciclo organizado do mais elementar ao mais maduro em uma empresa fictícia, a Imbróglios no Código LTDA, que estava à beira da falência, mas que acabou se organizando e utilizou a CI como o ponto inicial de sua alavancagem e crescimento Financeiro, dos 10 Analistas Iniciais dentro do Estado do Rio de Janeiro, a Imbróglios hoje em dia emprega mais de 3.000 profissionais de TI nos cinco continentes, muitos dos Analistas da época tornaram-se gestores. A Imbróglios no Código estava à beira da falênca, mas não por erros apenas de seus profissionais de desenvolvimento, mas por não disporem de uma infraestrutura competitiva. Enfim, a empresa havia parado no tempo e começou a ouvir as opiniões de seus colaboradores, em reuniões diárias, como último recurso para evitar a completa bancarrota, nestas  reuniões a palavra Integração Contínua e outros termos sobre o mesmo assunto foram mencionadas e também os gestores identificaram que membros da equipe já detinham tal conhecimento oriundo de experiências anteriores, mas que no passado a gerência não os permitia implementar tais idéias porque "não havia espaço no cronograma e o tempo era curto". Apesar de uma parte da equipe declarar oposição diante desta medida, que veremos como tal resistência foi combalida, a Diretoria comprou a idéia e deu um prazo de 5 meses a contar daquele momento e transcrevemos abaixo os trechos mais importantes da consultoria na qual este autor tomou parte e participou do processo para que o leitor acompanhe ciclo por ciclo como a Integração Contínua pode mudar a filosofia de trabalho de uma organização.
Primeiro Ciclo. Não existe Servidor de Build. Inicialmente, a equipe ainda não dispõe de nenhum tipo de Servidor Central de builds. O Software é compilado e construído manualmente na máquina do desenvolvedor, mesmo que se use um Script NAnt ou similar. O Código Fonte dos Projetos é armazenado em servidores centralizados, mas desenvolvedores nem sempre commitam suas alterações neste Servidor. Antes que uma versão estável ser liberada, um desenvolvedor executa a integração, das alterações no código fonte da aplicação, de forma manual, este processo gera um grande prejuízo no que tange a ultima parte: integração manual de código.
Segundo Ciclo. Builds Noturnos ou Diários (Nightly ou Daily). O time agora dispõe de um servidor para builds e builds automatizados são agendados em um período regular, sendo tipicamente à noite ou durante o dia quando o fluxo de trabalho é interrompido por algum período - horario de almoço coletivo, reunião diária entre equipes ou afins, algo que cause queda de utilização do link. Estes builds apenas compilam os códigos, ainda não existem Testes Unitários sendo executados. Testes Unitários quando existem, não são parte integral ou obrigatória do processo de build. Desenvolvedores commitam suas alterações de Código Fonte regularmente, normalmente, no final do dia. Se um membro da equipe de desenvolvimento efetua um commit de seus códigos e este conflita com o trabalho de outro membro da equipe, o servidor de builds alerta toda a equipe via e-mail na manhã seguinte. Porém, faz-se mister que o time ainda utilize o servidor de builds com o único propósito de informar as quebras e nada mais, logo a equipe acaba se sentindo desobrigada à corrigir toda a compilação interrompida para corrigir apenas aquele build em sua estação de trabalho, e o build pode ficar quebrado no servidor de por um tempo maior.
Terceiro Ciclo. Builds Noturnos e Automação de Testes Básicos. O time agora já está começando a tirar proveito da Integração Contínua com Testes automatizados mais seriamente. O servidor de build está configurado para dar início a um novo build assim que um novo código é commitado para o SCM e aos membros do time é permitida a livre visualização da alteração do código gerou a execução do Build e qual o impacto desta mudança. Além disso, o script que executa o build compila a aplicação e executa um conjunto de testes unitários ou testes de integração automatizados. Com relação ao envio de mensagens à equipe, o servidor de builds também envia um alerta aos membros do time sobre problemas na integração utilizando não só o e-mail, mas como outras formas mais proativas como mensageiros instantâneos. Builds que ficam quebrados agora são corrigidos mais rapidamente.
Quarto Ciclo. Tem início a Revolução das Métricas. Agora a equipe dispõe de verificação da Qualidade de Código automatizada e validação de métricas quanto à cobertura do código junto às melhores práticas, auxiliam muito na verificação para a eficiência dos nossos testes. Existe um build que incide apenas sobre a verificação da qualidade do código, ou seja, em um processo que é executado de forma distinta dos outros builds mostrados anteriormente, este ocorre sempre, de forma separada, sobre a versão mais estável que está sendo executada, também gera uma documentação sobre a API utilizada para a criação da aplicação, facilitando assim a organização do código e escalabilidade da infraestrutura da aplicação ao longo do tempo o que facilita a chegada de novos membros à equipe e diminui o tempo de treinamento do novo recurso. Este tipo de implementação da CI ajuda a equipe a manter a qualidade do código sempre em alto nível, informando ao time quais métricas/práticas de testes estão sendo eficientes e úteis ao processo. O time também dispõe de um Painel de Acompanhamento de Builds, que consiste em mostrar os resultados de todo o Projeto em um Monitor na Sala ou no corredor da empresa de forma que todos tenham visibilidade de todos os processos de CI que estejam sendo executados e os status destes.
Quinto Ciclo. Testando de forma mais aprofundada e automatizada. Os benefícios da Integração Congtínua são intimamente relacionados à práticas de testes bem consolidadas dentro da equipe. Agora, práticas como Desenvolvimento Orientado a Testes, TDD, são largamente utilizados resultando em um crescimento na confiança dos resultados dos builds automatizados. A aplicação não é mais simplesmente compilada e testada, mas se o teste tem sucesso, este é automaticamente implementado para um servidor de aplicação para testes de outros tipos tais como testes de Aceitação e Testes de Desempenho e chegando a este ponto, já podemos começar o 6º e penúltimo ciclo, o dos Testes de Aceitação e Implementação que levará a empresa à filosofia da Entrega Contínua.
Sexto Ciclo. Testes automatizados de Aceitação e uma implementação melhor elaborada. O Desenvolvimento Orientado à Testes de Aceitação é praticado, guiando esforços de desenvolvimento e providenciando sempre relatórios de alto nível do estado atual do Projeto. Estes testes automatizados utilizam ferramentas para Desenvolvimento Orientado a Comportamentos, BDD, que agem como agentes para comunicação e documentação assim como as ferramentas de testes que publicam relatórios sobre os resultados de testes nos termos de negócio que profissionais que não sejam de desenvolvimento possam entender. Uma vez que testes de alto nível sejam automatizados dentro do projeto o mais cedo possível no processo de desenvolvimento, eles também dão a idéia de que melhorias foram implementadas e o quanto falta para a entrega da iteração ou do Projeto como um todo. A aplicação é automaticamente implementada no ambiente especifico para testes junto ao pessoal da Qualidade, ou QA, tão logo as alterações sejam commitada no SCM ou em uma base de build noturno. Uma versão estável do software projetado poder ser implementada em ambiente controlado, como pré-produção por exemplo, utilizando um build executado manualmente, caso a Equipe de Testes julgar estável. A equipe de desenvolvimento também será capaz de utilizar o servidor de build para proteger a versão estável gerando um rollback para uma versão anterior, se algum erro brusco e crítico ocorrer.
Sétimo Ciclo. Entrega Contínua. Existe um confiança colocada nos testes automatizados de Unidade, de Integração e de Aceitação de tal forma que as equipes podem aplicar as técnicas de implementação automatizada no Ciclo 6, porém, caso tudo corra bem, podem permitir que o CI implemente-as diretamente em produção em caso positivo dos builds. É este tipo de procedimento que configura a Entrega Continua e pelo que pudemos ver no decorrer deste processo,a única intervenção humana neste processo foi executada no ciclo 6 para testes de implementação.
Será dentro destes ciclos que nossa série de artigo irá funcionar, utilizaremos estes 7 ciclos de forma a mostrar de forma prática para o leitor como que o CI vai evoluindo dentro da Organização e como o nosso script de build vai sendo incrementado durante a implementação das melhorias. Porém, diante de toda esta revolução como foi visto anteriormente, grandes objeções surgem por estarmos lidando com o fator humano que não é feito à mudanças. Podemos vencer os mais resistentes com simples respostas para complexas perguntas e será o que veremos à seguir.

Lidando com a resistência às mudanças
Com todos estes builds acontecendo e membros da equipe tendo que modificar a sua rotina e efetuar atualizações em seus códigos, com os códigos mais atuais para se manterem atualizados motivados por um servidor de SCM, de forma freqüente, ou seja, quase que diariamente, podemos encontrar certas objeções dos membros do time e as mais comuns veremos a seguir.
CI aumenta a manutenção. Algum membro terá de manter o Sistema de CI. Tal procedimento fará com que este membro saia de seu escopo de ser apenas um desenvolvedor. No início, existirá um tempo maior que gerará um excesso de trabalho ao membro da equipe, porém, o time terá uma grande economia de tempo posto que o grande gargalo conhecido, que era o da Integração manual, não irá ocorrer e tornará o teste da aplicação mais simples. Muitas equipes informam, e isso é facilmente encontrado na Grande Rede, que após o processo de CI ser implantado, as rotinas de manutenção demoram menos que uma hora por semana na média.
Estas mudanças são rápidas demais e são muitas. É difícil se adaptar a uma nova forma de se fazer algo. Não implementar tudo de uma só vez. Começar com um build simples executado uma vez ao dia e então adicionar um teste unitário. Após o time se sentir mais confortável e se acostumar com esta rotina podem adicionar um ou mais builds por dia ou começar a executar uma Análise de Código. Por implementar o processo em pequenos passos e mostrar que estes pequenos passos nos retornam grandes resultados - principalmente em tempo e facilidade, teremos mais aceitação dentro deste novo processo.
CI implica em uma custo adicional de hardware e de software. Comece com um PC antigo como seu servidor de CI se conseguir. Eventualmente, vamos precisar melhorar o hardware para que possamos executar builds de forma mais veloz (devemos nos lembrar de que um build de integração, demora no mínimo, 5 minutos), mas para builds executados de duas à três vezes ao dia, máquinas antigas também vão nos atender. Se as ferramentas que informarmos nesta série forem utilizadas como recomendamos, o seu custo com software será mínimo.
Desenvolvedores devem compilar e testar. Não estamos tirando estas responsabilidades dos desenvolvedores. Estamos apenas movendo boa parte do trabalho mais demorado e oneroso para um Sistema automatizado. Tal medida fará com que os membros da equipe possam produzir mais e resolver melhor os problemas de Regras de Negócios que existam na aplicação, logo esta medida fará com que os desenvolvedores fiquem mais produtivos aonde importa: Escrever e identificar erros no código.
O Projeto é longo demais ou está adiantado demais para que adicionemos o CI. Embora seja muito melhor e mais fácil se colocar um projeto na CI desde o seu início, a verdade é que, muito do trabalho que é feito é justamente dentro de manutenção dos nossos Projetos já existentes. Um projeto que já exista pode não ter Testes Unitários, mas podemos utilizar um SCM e precisar fazer builds destes. Podemos nos beneficiar da CI não importando o tempo e o tamanho do Projeto.
Se a CI for colocada em seu devido lugar, muitos dos problemas mais conhecidos dentro de um Projeto de Software: do desenvolvimento à entrega e manutenção devem ocorrer. Mais adiante, vemos muitas razões para que se utilize o CI em seu processo de desenvolvimento.
Riscos Reduzidos. Implementando bons processos de CI, criaremos software com uma performance muito melhor que a habitual porque já teremos feito os testes unitários e testes de integração de código muito mais cedo dentro do processo, então aumentamos as chances de corrigir erros também muito cedo. Falaremos sobre Riscos na próxima e derradeira seção desta primeira parte.
Produto implementado de forma simples. Se automatizarmos o processo de instalação. Nós saberemos sobre o comportamento de sua instalação antes de receber um feedback de problemas do cliente.
Visibilidade de Projeto. O mecanismo de feedback permite aos membros da equipe saber sobre os resultados do build e onde estão os problemas. Os problemas podem ser corrigido o quanto antes e esta abordagem reduz custos e o tempo gasto corrigindo problemas.
Não permita que as objeções dos membros nos obrigue a recuar ou resistir. A resistência inicial irá diminuir gradualmente quando a equipe começar a trabalhar diretamente com o Sistema de CI. Uma Terapeuta Familiar muito conhecido em outros países chamada Virginia Satir criou o Modelo de Mudanças Satir que se refere sobre como as famílias lidam com a mudança. E transportando algumas diretrizes para lidar com a equipe, temos a seguir algumas boas recomendações. E este processo de mudança envolve cinco passos.
Passo 1. Status Quo Tardio. Todos estão trabalhando normalmente no processo atual e todos sabem como ele funciona.
Passo 2. Resistência. Um novo elemento é introduzido no seio da equipe. As pessoas são sempre hesitantes para mudar a sua rotina. Se o status quo funciona muito bem, por que modificá-lo?
Passo 3. Caos. O novo elemento introduzido é adotado. Não há uma forma normal de fazer novas coisas. Rotinas diárias são despedaçadas.
Passo 4. Integração. Pessoas se ajustam lentamente ao novo modo de se fazer coisas. Fica mais fácil fazer o trabalho de convencimento se utilizar esta abordagem. E começam a utilizá-lo, mas de forma gradual.
Passo 5. Novo status quo. O novo elemento já está totalmente integrado ao Sistema. As pessoas agora o vêem como Normal e voltamos ao passo 1.
Quase todos os times acabam adotando novas metodologias de uma hora para outra. Este processo por soar familiar. Assim como lutamos contra a resistência da equipe, sejamos persistentes em implementar as mudanças. Os membros do time em breve vão aceitá-las. Alguns membros vão adotar o CI mais rápido que outros alguns podem precisar de uma dose maior de convencimento. Talvez seja necessário mostrar a eles como o CI reduz os riscos e os custos e benefícios dentro do processo de desenvolvimento.

Custo Benefício da Integração Contínua
Seu cliente não gosta de riscos. Seu gerente não gosta de riscos. Seu Gerente de Projetos precisa ter planos para mitigar os riscos. No fim, não vamos gostar de riscos. CI é sempre apontado como uma redução nos riscos. Talvez o maior risco dentro do desenvolvimento de software é o não cumprimento de prazos, em outras palavras o projeto sendo entregue mais tarde do que o acordado e contratado. Por causa do mecanismo de feedback no processo de CI, os membros do time sempre vão saber do estado do build atual, que nos auxilia a saber se o projeto está dentro dos prazos.
Veremos o mecanismo de feedback mais adiante em nossa série de artigos.
O outro grande risco é o risco de defeito, os chamados bugs. Como já mostramos anteriormente, que quanto mais tardia é a política de defeitos aplicada a um projeto, maior será o custo para corrigi-los. Algumas estimativas sugerem que o custo para a correção de um defeito gira em torno de R$3.500,00 em média para corrigir um simples defeito de software em aplicações internas. Anos atrás, uma companhia de antivírus bem conhecida gerou defeito dentro de um de seus pacotes de atualização. Este pequeno defeito fez com que os clientes perdessem confiança naquele antivirus e forçaram a empresa a reduzir seus rendimentos trimestrais e previsões orçamentárias de receita em US$8.000.000,00. Não gostaríamos de ver a nossa empresa passar por algo parecido.
Uma das vantagens da CI neste ponto é que os defeitos são corrigidos tão logo eles são identificados. Integrando e testando o software a cada build, podemos identificar e corrigir defeitos no processo. Discutiremos Testes Unitários mais adiante em nossa série de Artigos.
Sempre fizemos algumas perguntas para saber o andamento de nosso projeto como: consideramos quantos caminhos diferentes podem existir em nosso código fonte? testamos a combinação de cada if/else? Quantos casos de declarações switch foram testadas? respondemos estas perguntas implementando a Cobertura de Código.
Cobertura de Código é a metodologia que verifica quais caminhos serão testados e quais não serão. Uma grande solução quando o assunto é cobertura de código é que podemos automatizá-la em nosso processo de CI. É humanamente impossível de serem testadas todas as combinações de cobertura existentes, mas quanto mais for testado, menos problemas serão descobertos pelo seu cliente.
Outro risco conhecido são as atualizações em Bancos de Dados. Nunca foi fácil adicionar campos para uma tabela ou novas tabelas em um Banco de Dados.
Com Integração Contínua em Banco de Dados, teremos ciência se as mudanças implementadas trabalham corretamente e sem perda de dados. Demonstraremos Integração Contínua em Bancos de Dados mais adiante nesta série.
Desenvolvedores odeiam padrões de codificação e arquiteturais, mas têm seu propósito útil: permitem que a aplicação siga as melhores práticas, fazem com que a aplicação funcione melhor e fica mais fácil de manter. Revisões de Código identificam muitos problemas; mas, como Revisão de Código é um processo manual, algumas coisas podem se perder. Então, por que não automatizar a verificação da padronização em seu processo de CI? cobriremos também a Revisão de Código mais adiante nesta série.
Comentários são colocados raramente no código e a documentação quase não existe. Muitos dizem que se formos ágeis, não precisamos de documentação, mas isso não é verdade. O Manifesto Ágil diz que o trabalho é mais valorizado que a documentação. Mas, a documentação é ainda necessária, especialmente se estivermos criando assemblies a serem utilizados por outras equipes ou outras pessoas em forma de APIs. Esta é outra oportunidade para automatizar seu processo com processos de CI. Cobriremos esta parte também nesta série de artigos mais adiante.
Como saberemos que o processo de instalação/implementação funcionou corretamente? Existem poucas coisas que frustram mais usuários do que não conseguirem instalar ou implementar uma aplicação. É fácil ver problemas escondidos no Projeto que sem um sistema de CI só seria identificado muito depois do processo de desenvolvimento, quando os problemas já se tornaram bem mais difíceis e caros para se corrigir. Agora que já sabemos o que a Integração Continua é e como ela pode melhorar nossos processo de desenvolvimento nas próximas partes veremos o processo de CI ser implementado gradualmente seguindo os ciclos informados nesta primeira parte.
Nesta primeira etapa de nossa série sobre a Integração Contínua, vimos os grandes problemas que são encontrados em projetos, organizações e equipes de forma que preparamos o leitor para os desafios que serão encontrados nesta busca pela excelência em seus projetos e tal excelência começa com o Sistema de Integração Contínua. O maior obstáculo para a implementação não está no Projeto em si, mas na resistência das equipes em implementá-lo dentro das organizações.
Assim que a resistência é combalida demonstrando no dia a dia como a CI é útil e viável, a implementação começa a mudar de forma lenta, mas drástica a forma como as organizações interagem dentro do Projeto de Software. Tendo mais agilidade, as equipes conseguem melhores resultados. 

sexta-feira, 23 de agosto de 2013

Spring.NET: Uma introdução



O Spring.NET é um port para a linguagem .NET do famoso Projeto do Framework de integração, o Spring, da empresa SpringSource, atualmente uma empresa do grupo VMWare, para a linguagem Java, tal framework já é bastate consolidado no meio Java até por, na época de sua criação, implementar tecnologias que a linguagem não tinha como padrão em sua JDK, tecnoogias estas que atualmente já fazem parte de projetos da comunidade principalmente a CDI - Component Dependency Injection, que hoje é uma JSR na comunidade Java, sendo largamente implementada agora na versão 6 de seu JDK. O Spring Framework também deu início ao Projeto para o Enterprise JavaBeans 3.0 onde utiliza muitos padrões de projeto implementados no Spring Framework e o principal deles é justamente a Injeção de Dependências, sendo esta a base de sua integração. Porém, a mesma tecnologia sendo portada para .NET ainda não teve o mesmo sucesso e nem mesmo a mesma quantidade de adeptos.
Muito disso se deve a existência de outros frameworks e conteineres para injeção de dependências tais como: Ninject, Castle Windsor dentre outros já bastante conceituados no mundo .NET. O Spring.NET pode facilmente ocupar este espaço por ser um framework de "curta curva de aprendizado" o que implica que o mesmo é bem mais fácil de se aprender e de se executar e também, a injeção de dependências. A Injeção de Dependências é apenas um de seus vários módulos e é a sua base principal. Diante disso o Spring.NET tira toda a sua vantagem diante dos outros frameworks de Injeção de dependências porque o Spring.NET não faz só este trabalho, faz mais do que isso: integra desde Camada de Acesso a Dados à Camada de Apresentação, bem como Queue Messaging, WebServices, Integração com WCF e por aí vai, utilizando o Spring.NET como integrador de suas aplicações, ele permite e garante a estabildade permitindo ao desenvolvedor que não se preocupe com a sua infraestrutura: coleta de lixo, instanciação de objetos, performance e escalabilidade. Qual seria a vantagem de se usar o mesmo framework para Integrar todas as camadas? creio que todo desenvolvedor ou Arquiteto procura algo deste porte para sua aplicações, o que será decisivo para o uso ou não do Spring.NET será apenas a arquitetura e principalmente o custo e o tempo do Projeto. Entendendo bem como funciona a Injeção de Dependências, o desenvolvedor consegue dominar o Spring.NET por completo.

Os Módulos do Spring.NET
O Spring.NET detém 18 módulos de Integração que participam de todo o ciclo de vida de um projeto: de sua análise até os teste finais para entrega do software. Vamos listá-los:
- Arquitetura de Base
Esta arquitetura é a que sustenta o Spring.NET e sobre ela que as integrações são executadas.
1) Core - Núcleo principal que é utilizado como Container para Injeção de Dependências e as funcionalidades principais, que são as interfaces de uso para esta infraestrutura de Injeção.
2) AOP - Aspect Oriented Programming - Programação Orientada a Aspectos contendo suas Suporte a Modelo AOP, Infraestrutura utilizando o Padrão de Projetos Proxy e a Biblioteca Aspect Library. O Spring.NET é um dos poucos frameworks que pode ser utilizado como container AOP também, esta tecnologia na linguagem Java já é bastante consolidada e utilizada, por isso ela também foi trazida para o Spring.NET para que suas facilidades em Projetos também sejam exploradas pelas Equipes que utilizam o .NET.
- Arquiteturas implementadas pelo Spring.NET para integração.
São as arquiteturas de integração propriamente ditas na qual o Spring disponibiliza tendo esta integração sempre facilitada pela Injeção de Dependências. Temos os seguintes módulos:
a) Camada de Dados (Gerenciamento de Transação): NHibernate;
b) Mensageria (Messaging) (Utilizando sempre através do Microsoft MSMQ): Apache NMS, Tibco EMS;
c) Serviços (Abstração de Serviços Portáveis, ou seja, WebServices, SOA e afins): Enterprise Services, Web Services e Serviços WCF.
d) Camada de Apresentação: MVC
e) Teste Unitário: NUnit e MSTest
f) Scheduling ou Agendamento de Tarefas: Quartz.NET
Neste artigo, vamos implementar a Arquitetura básica e o Módulo de Core. Em artigos futuros falaremos de todos os Módulos e arquiteturas.


Imagem 1. Módulos do Spring.NET


A Imagem 1 nos dá uma impressão do tamanho deste framework e suas implementações internas.

Injeção de Dependências com o Spring.NET

O Spring.NET e seu assembly Spring.Core implementam a Injeção de Dependências utilizando os dois tipos existentes: via Setter e via Construtor. Tomemos como exemplo uma Classe que fará a persistência de um Cliente e uma Classe que será responsável por abrir a conexão com a Base de Dados. Para este exemplo usamos as Classes respectivamente: ClienteDAL e Conexao. Estas duas Classes nos farão compreender melhor os dois tipos de Injeção de Dependências existentes e como estes são utilizados pelo Spring.NET.
Injeção de Dependências via Construtor
Neste tipo de Injeção, A Classe injetada estará encapsulada dentro de um parâmetro que será passado ao construtor, lembrando que este parâmetro deve ser tipado com a Classe a ser injetada, como mostram os Trechos de Código 1 e 2:

Trecho de Código 1. Classe Conexao
 public class Conexao{ 
  #region Construtor
  //Digitando ctor e pressionando a tecla TAB duas vezes
  //O Visual Studio cria um construtor padrão      
  public Conexao() 
  {
     //Algoritmo para acesso ao Banco de Dados
  }
  #endregion
 }

Esta nossa Classe Conexão fará o acesso ao Banco de Dados e será necessária em nossa classe de persistência. Esta será injetada no construtor da Classe ClienteDAL via parâmetro tipado como Conexao. Como mostrado no Trecho de Código 2.

Trecho de Código 2. Injeção da Classe Conexão no construtor de ClienteDAL 
#region Propriedades
        /// <summary>
        /// Propriedade que será utilizada para Injeção via Construtor
        /// </summary>
        public Conexao conector { get; set; }
        #endregion

        #region Construtores

        public ClienteDAL()
        {

        }

        /// <summary>
        /// Construtor para Injeção de Dependências 
        /// via Construtor
        /// </summary>
        /// <param name="conexao"></param>
        public ClienteDAL(Conexao conexao)
        {
            this.conector = conexao;
        }
#endregion
 

Neste ponto, observando o construtor da classe ContatoDAL, podemos notar que o parâmetro chamado conexao está tipado com a nossa Classe Conexao, logo já temos a nossa injeção de dependências pronta para uso, o Spring.NET utiliza do padrão singleton juntamente com o Service Locator, ou seja, abre apenas uma instância desta Classe diretamente, caso a mesma não esteja instanciada, ela o é automaticamente, sempre que o construtor é ativado. O que torna o uso bastante racional. Pronto, agora você já pode usar seus métodos de persistência preocupando-se apenas com as operações de CRUD.
Injeção de Dependências via Setter
Nesta modalidade de Injeção de Dependências, utilizamos um "setter" para que nele possamos Injetar a Classe que proverá todo o serviço necessário. Como podemos ver no Trecho de Código 3.
Neste modo, podemos utilizar uma interface para enriquecer a nossa codificação, posto que no momento da implementação da Injeção, podemos fazer com que a Classe Injetada possa utilizar a interface facilitando a abstração em um ambiente de “Mundo Real”. Neste momento, vamos utilizar esta Injeção via Setter em uma Classe de Regra de Negócio que irá utilizara a Interface IClienteDAL para prover a injeção de dependências para ClienteDAL


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using IntroSpringNet.Modelo;

namespace IntroSpringNet.Persistencia
{
    public interface IClienteDAL
    {
        void Salvar(Cliente cliente);
    }
}

Agora que criamos a nossa interface, vamos criar usar um trecho de código para exemplificar esta injeção.
#region Propriedades

public IClienteDAL DAL { get; set; }

#endregion

#region Métodos

public Boolean CadastrarCliente(Cliente cliente)
{
  DAL.Salvar(cliente);
  return true;
}

#endregion


Podemos ver na que a propriedade DAL recebe a Interface IClienteDAL onde receberá a injeção de ClienteDAL. A Classe CadastrarCliente, utiliza DAL e não há a necessidade de instanciar o objeto, posto que o Spring já está controlando o objeto em seu contêiner via setter.
Como o Spring gerencia os Objetos para a DI.
O Spring.NET tem uma forma peculiar de gerenciar seus objetos, todos estes objetos são registrados em um arquivo .xml, ele pode ser os descritores do próprio .NET, ou seja, os arquivos .config – App.Config se standalone ou Web.Config se Web. Pode-se utilizar estes arquivos ou neste arquivos fazer um apontamento para outro XML, posto que o Spring.NET tem um cabeçalho bem peculiar, no Trecho de Código 4, temos um exemplo de arquivo .XML com header e configuração de gerência de objetos registrados. Sem este arquivo, o Spring.NET não poderá funcionar posto que nele estão todas as funcionalidades necessárias para que o contêiner seja inicializado e que o mesmo reconheça todos os objetos através do parsing, ou seja, o arquivo é totalmente validado antes de ser utilizado

Trecho de Código 4. Configuração do Spring.NET em arquivo de configuração XML
<configSections>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler
               , Spring.Core"/>
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler
               ,Spring.Core"/>
    </sectionGroup>
  </configSections>
...
<spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <!-- Objetos para Injeção de Dependências -->
    <objects xmlns="http://www.springframework.net">
      <!-- Conexao -->
      <object name="Conexao" type="DAL.FonteDeDados.Conexao, DAL"/>
            
      <!-- ContatoDAL -->
      <object name="ContatoDAL" type="DAL.Persistencia.ContatoDAL, DAL">
        <constructor-arg ref="Conexao"/>
      </object>
      
      <!-- ManterContato -->
      <object name="ManterContato" type="BLL.Negocio.ManterContato, BLL">
        <property name="Dao" ref="ContatoDAL" />
      </object>
      
    </objects>
  </spring>
...

Dentro da tag configSection no início do arquivo, temos a tag sectionGroup, dentro desta temos mais dois sections com o atributo de name, estes atributos significam que neste arquivo temos o acesso ao Contexto e que nele também existem objetos a serem gerenciados pelo Conteiner de Injeção de Dependências. Com relação ao Contexto, significa que temos de chamar a interface IApplicationContext dentro de nossa aplicação para que o Spring.NET possa acessar as configurações deste arquivo para iniciar o contêiner de Injeção de Dependências.
Na seção que contêm a tag de nome Spring, temos a configuração e registro dos objetos que serão utilizados pelo contêiner de DI. Veremos este trecho mais de perto através do Trecho de Código 5.

Trecho de Código 5. Seção de Configuração do Spring e seus objetos
<spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <!-- Objetos para Injeção de Dependências -->
    <objects xmlns="http://www.springframework.net">
      <!-- Conexao -->
      <object name="Conexao" type="DAL.FonteDeDados.Conexao, DAL"/>
            
      <!-- ContatoDAL -->
      <object name="ContatoDAL" type="DAL.Persistencia.ContatoDAL, DAL">
        <constructor-arg ref="Conexao"/>
      </object>
      
      <!-- ManterContato -->
      <object name="ManterContato" type="BLL.Negocio.ManterContato, BLL">
        <property name="Dao" ref="ContatoDAL" />
      </object>
      
    </objects>
  </spring>
Nesta seção, a tag context logo abaixo do inicio da seção completa o que está presente na configSection do Trecho de Código 4, onde temos o atributo chamado object. Ambas context e resource estão interligados a esta configSection.
Com relação aos objetos gerenciados pelo Spring se encontra dentro da tag object que é registrada através de objects  onde tem o nome de serviço apontando para HTTP://www.springframework.net, neste momento, iniciando o registro das Classes no contêiner de DI do Spring.
Cada tag object contém alguns atributos, sendo os mais importantes name e type. Name recebe um valor qualquer, como uma identificação, ela não precisa ser igual ao nome da Classe, mas por convenção o segue apenas para questões de documentação caso existam muitos em um arquivo de gerenciamento. Já type, somos obrigados a colocar o nome completo da classe, seguido de uma virgula (,) e o nome do assembly,como visto no Trecho de Código 6.

Trecho de Código 6. Um objeto simples registrado.
<!-- Conexao -->
<object name="Conexao" type="IntroSpringNet.Persistencia.Conexao, IntroSpringNet"/>
O objeto em questão significa que para o Conteiner a Classe DAL.FonteDeDados.Conexao no Assembly DAL foi registrada com o nome de Conexao
 Caso a tag object só tenha estes registros signfica que é apenas uma Classe sendo apontada e nada mais. Porém, quando fazer as injeções via setter ou via Construtor precisamos colocar mais uma tag dentro de object para informar ao Spring que dentro desta Classe gerenciada, o contêiner ainda precisa efetuar a Injeção de Dependencias. No Trecho de Código 7, vemos um exemplo de Injeção via Construtor das classes criadas nos Trechos de Código 1 e 2.

Trecho de Código 7. Injeção via construtor registrada no arquivo de configuração do Spring.NET
<!-- ClienteDAL -->
<!-- Injeção via Construtor -->
<object name="ClienteDAL" type="IntroSpringNet.Persistencia.ClienteDAL, IntroSpringNet">
<constructor-arg ref="Conexao"/>
</object>
Este objeto registrado no Trecho de Código 7 pela sua implementação nos mostra que não é um objeto simples, na tag constructor-arg significa que a Classe informada tem um construtor e que o parâmetro injetado é a classe registrada com o nome de Conexao, pois ref significa “fazer referência a um objeto registrado no container”. Então caso a interface IApplicationContext registre o objeto de nome ClienteDAL, ele automaticamente registrará dentro da Classe ClienteDAL, em seu construtor o tipo do argumento será a classe que está atrelada ao nome Conexao e para nosso entendimento é o mesmo objeto registrado no Trecho de Código 6, ou o nosso objeto simples.
A diferença no registro via setter é que ao invés de constructor-arg apenas chamamos a tag property, apontando a propriedade onde a Classe é injetada no setter. Como nos mostra o Trecho de Código 8.

Trecho de Código 8. Injeção vai Setter registrada no contêiner do Spring.NET
<!-- ManterContato -->
<!-- Injeção via Setter -->
<!-- Injeta ClienteDAL dentro de DAL
 DAL é tipada pela interface, logo a mesma é preenchida por ClienteDAL
-->
<object name="ManterCliente" type="IntroSpringNet.Negocio.ManterCliente, IntroSpringNet">
  <property name="DAL" ref="ClienteDAL" />
</object>

Este objeto informa que a propriedade DAL receberá a injeção do objeto ClienteDAL mostrado no Trecho de Código 7.

Implementação do Acesso aos Objetos

Agora faremos com que todas estas configurações acima funcionem plenamente. Neste trecho, vamos utilizar a fachada IApplicationContext do Spring para acessar o contêiner de Injeção de Dependências. Como no exemplo do Trecho de Código 8.


Contato contato = new Contato();
contato.NomeCompleto = "Luis Gabriel Nascimento Simas";
contato.Email = "gabrielsimas@gmail.com";
contato.Linkedin = "http://br.linkedin.com/in/gabrielsimas/";
contato.Twitter = "@gabnascimento";
contato.Facebook = "http://www.facebook.com/luisgabrielsimas";

IApplicationContext di = ContextRegistry.GetContext();
IContatoDAL dal = (ContatoDAL)di.GetObject("ContatoDAL");

dal.Salvar(contato);

Em di, lemos o arquivo de configuração, em dal, carregamos o objeto gerenciável do arquivo de configuração que contém o id contendo o valor “ContatoDAL”, o mesmo objeto do Trecho de Código 7.

Conclusão

Neste artigo bastante introdutório sobre o Spring.NET, vimos um pouco de sua base principal, que é a Injeção de Dependências, em artigos futuros falaremos de cada um dos módulos do Spring.NET de forma bastante prática. 

Referências
Spring.NET Framework - http://www.springframework.net/
Documentação do Spring.NET - http://www.springframework.net/documentation.html
Injeção de Dependências/DI de Martin Fowler - http://www.martinfowler.com/articles/injection.html

Introdução ao NHibernate



Neste post, mostrarei uma pequena introdução ao NHibernate que é um framework de ORM portado para a plataforma .NET originário do framework Hibernate, um framework de bastante sucesso e utilizado até então, para a linguagem Java. Diante deste port, o NHibernate ainda é um projeto novo em constante evolução, tanto que ainda não acompanha seu “irmão” mais famoso com as grandes facilidades e opções de mapeamento mais rápido, principalmente com o uso de anotações, que em .NET são chamados de atributos – São interfaces que são utilizadas para “decorar” Classes, um substituo dos arquivos XML, tão odiados pelos programadores –que facilitam muito o dia-a-dia.
Tal framework foi criado pela JBoss, criadora do Hibernate, porém, com o tempo foi criada a NHForge que cuida diretamente do controle deste framework em site diferenciado, o NHForge.org.

Estrutura do NHibernate

Vemos a estrutura básica do NHibernate na Imagem 1.
Imagem 1. Diagrama arquitetônico do NHibernate

Na Imagem 1, podemos observar que o NHibernate é acoplado diretamente entre O Banco de Dados a sua aplicação, porém, podemos observar também que os objetos persistentes – persistent objects – são utilizados através da aplicação e, através de conexão com o NHibernate, nos dá a entender que são as peças mais importantes desta engrenagem. Podemos também observar que dentro do domínio do NHibernate vemos o mapeamento em arquivos XML – Seção XML Mappings e o arquivo de configuração do .NET, App.config ou Web.config. Fique certo de que o NHibernate, como uma Camada de Acesso aos Dados e, posteriormente de persistência, de sua aplicação, estão fortemente acoplados porque todas as outras camadas, caso existam, são totalmente dependentes destes objetos de persistência para E/S de dados. Seria muito interessante mostrar uma visão mais detalhada da arquitetura em tempo de execução do NHibernate, porém isso não é possível pelas formas diferentes de abordagens e configurações, destas separamos duas: As arquiteturas simples e detalhada.
Na sua arquitetura simples, faz com que a aplicação disponibilize suas próprias conexões utilizando o ADO.NET e gerencie suas próprias transações. Esta abordagem usa um pequeno conjunto de APIs do NHibernate, como podemos verificar na Imagem 2.

Imagem 2. Subconjunto de APIs do NHibernate


Na arquitetura Detalhada, existe uma abstração da aplicação totalmente diferente do uso das APIs do ADO.NET e deixa o NHibernate controlar e cuidar dos detalhes, permitindo assim que o mesmo cumpra o seu papel de Framework ORM, estes objetos dos quais o NHibernate faz uso internamente mostrados na Imagem 3 e posteriormente explicados um a um.


Imagem 3. Objetos presentes na Arquitetura Detalhada do NHibernate

Abaixo, listamos alguns dos objetos mais importantes presentes na Imagem 3:

ISession (NHibernate.ISession)
Um objeto single-threaded, ou seja, exclusivo por instância, e de curta duração que representa a conversa entre a aplicação e a unidade de persistência. Encapsula uma conexão ADO.NET. É uma factory para ITransaction. Mantém um cache de primeiro nível, mandatório, ou seja, sob demanda, de objetos persistentes. Este cache é utilizado quando o grafo de objetos é utilizado ou busca de objetos por identificador.

Persistent Objects and Collections
São objetos contendo estados de persistência e funções de negócio, tais objetos são sempre de curta duração e single-threaded. Estes podem ser Classes POCO, o que mais importa sobre estes é que são sempre associados com uma, somente uma ISession. Assim que uma ISession é finalizada, eles são desconectados e liberados para serem utilizados por quaisquer camadas na aplicação, isto quer dizer que pode ser utilizados como um DTO para transferência de dados de/para a Camada de Apresentação.

Transient Objects and Collections
Objetos Transientes (de curta duração, não persistidos em Banco de Dados) e Coleções
são instâncias de Classes que não estão atualmente associados com uma ISession. Eles podem ser instanciados ou não pela aplicação e sendo assim não foram ainda persistidos ou podem ser instanciados por uma ISession fechada.

ITransaction (NHibernate.ITransaction)
Esta Interface é opcional. É um objeto utilizado pela aplicação para especificar unidades de trabalho atômicas ou não. Aplica abstração de uma transação encapsulada do ADO.NET. Um ISession pode se utilizar de várias ITransaction em alguns casos.

IConnectionProvider (NHibernate.Connection.IConnectionProvider)
O Uso desta interface é opcional. Uma Factory para conexões e comandos ADO.NET. Aplica abstração de uma implementação específica e concreta de IDbConnection e IDbCommand. Não é exposto para a aplicação, mas, pode ser extendido/implementado pelo desenvolvedor.

IDriver (NHibernate.Driver.IDriver)
Uma interface que encapsula provedores ADO.NET diferentes, tais como convenções para nomes de parâmetros e convenções suportadas para uso com o ADO.NET. Seu uso é opcional.

ITransactionFactory (NHibernate.Transaction.ITransactionFactory)
Uma Factory para instancia de ITransaction. Não é exposta a aplicação, mas pode ser extendida/implementada pelo desenvolvedor. Seu uso é opcional.

Ao usar a arquitetura simples, a aplicação não utiliza nem ITransaction/ITransactionFactory e muito menos IConnectionProvider para utilizar o ADO.NET diretamente.

Neste artigo não utilizaremos todos estes objetos.

Classes POCO para uso com o NHibernate

Existem 4 regras principais para Classes POCO serem “reconhecidas” pelo NHibernate:

1) Use propriedades para campos persistentes.
2) Implemente um construtor padrão, ou seja, vazio.
3) Crie uma propriedade Identificadora, ou Id.
4) Utilize classes que não sejam sealed e use métodos virtuais.

Mapeamento do NHibernate

Atualmente, o mapeamento no NHibernate pode ser executado utilizando um arquivo XML ou com um novo projeto onde são utilizados atributos para decorarem as classes, primeiramente, vamos ao mapeamento utilizando o XML.

Mapeamento por Arquivo XML
O Mapeamento XML é a primeira etapa para a persistência com o NHibernate é onde tudo começa, na Código Fonte 1, temos um exemplo de cabeçalho estável para uso com o NHibernate. Por motivos de compilação, o NHibernate solicita que os arquivos para mapeamento contenham também a extensão .hbm como forma de ser reconhecido pelo assembly do ORM. Estes arquivos precisam ter sua propriedade Build Action setados como Embedded Resource, posto que no momento da compilação do código,  o arquivo seja reconhecido e convertido para um hierarquia de classes utilizando ponto, por exemplo: temos que mapear a Classe Pessoa para persistência, então criamos o arquivo pessoa.hbm.xml e nas suas propriedades, colocamos o atributo Build Action como Embedded Resource, como mostrado na Imagem 3.

Código Fonte 1. Cabeçalho de XML para mapeamento
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="DAL"
                   namespace="DAL.Entidade">
...
...
...
....
...
</hibernate-mapping>

Como podemos ver na Código Fonte 1, temos 2 atributos na tag hibernate-mapping que são assembly e namespace.
Assembly informa em qual Projeto estão localizadas as Classes a serem buscadas pelo NHibernate no qual está sendo apontada neste arquivo XML.
Namespace é o caminho completo de onde tais classes estão dentro de um Projeto. Lembrando que quanto mais informação neste sentido é menos tempo em que o compilador perde buscando as Classes para Mapeamento em memória.
Imagem 4. Arquivo XML configuração e nomenclatura padrão NHibernate

Neste ponto, já podemos mapear as nossas Classes de Persistência. Não é obrigatório criar um arquivo para cada Classe, podemos em um arquivo só inserir todas, mas, como melhor prática, é bom que seja criado um arquivo para cada Classe, desta forma, o entendimento do Sistema fica melhor e separa bem as responsabilidades. Na Código Fonte 2, temos um exemplo de Entidade na qual vamos mapear via XML.

Código Fonte 2. Classe Carro
public class Carro
    {
        #region atributos

        private Int32 id;
        private String modelo;
        private String motor;

        #endregion

        #region Propriedades

        public virtual Int32 Id
        {
            get { return this.id; }
            set { this.id = value; }
        }

        public virtual String Modelo
        {
            get { return this.modelo; }
            set { this.modelo = value; }
        }

        public virtual String Motor
        {
            get { return this.motor; }
            set { this.motor = value; }
        }

        #endregion

        #region Construtores

        public Carro()
        {

        }

        public Carro(String modelo, String motor)
        {
            this.Modelo = modelo;
            this.Motor = motor;
        }
        #endregion

        #region Sobrescritas
        public override string ToString()
        {
            return this.id + ", " + this.modelo + ", " + this.motor;
        }
        #endregion
    }
}
 

Para o correto mapeamento da Entidade, mapeamos apenas as Propriedades para o arquivo XML, como descrito na Código Fonte 3.

Código Fonte 3. Arquivo XML Carro.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="DAL"
                   namespace="DAL.Entidade">

  <class name="Carro" table="Carro">
    <id name="Id" column="id">
      <generator class="identity" />
    </id>

    <property name="Modelo" column="modelo" />
    <property name="Motor" column="motor" />
   
  </class>
 
</hibernate-mapping>

Na Código Fonte 3, temos algumas informações importantes, a tag id significa que estamos mapeando a propriedade de identificação única da Entidade, ou seja, valor exclusivo que identifica os dados gravados na Classe. Dentro desta tag, temos uma tag generator que como no Banco de Dados ela é um tipo de auto incremento, utilizamos o valor de atributo identity. Os valores possíveis e permitidos pela tag generator dentro do atributo class são:

increment - Gera identificadores de qualquer tipo desde que sejam únicos e que nenhum outro processo esteja inserindo dados na mesma tabela. Não use este valor com um Cluster.

identity - Suporta campos do tipo identity, ou seja, auto incremento nos Bancos de Dados DB2, MySQL, MS SQL Server e Sybase. O identificador que é devolvido pelo Banco de Dados é convertido para o tipo da propriedade usando Convert.ChangeType. Qualquer propriedade inteira é permitida.

sequence - Usa uma Sequence criada em DB2, PostgreSQL, Oracle ou um generator do Firebird. O identificador retornado pelo Banco de Dados é convertido para o tipo da propriedade usando Convert.ChangeType. Qualquer propriedade inteira é permitida.

hilo - Usa um algoritmo de hi/lo para gerar de forma bem eficiente qualquer tipo inteiro de qualquer Tabela ou campo (por padrão, hibernate_unique_key e next_hi respectivamente) como uma fonte de valores hi. O Algoritmo de hi/lo gera identificadores que são únicos apenas para um Banco de Dados em específico. Não use este hilo com uma conexão gerenciada pelo usuário, ou seja, quando as threads não são as mesmas.
Você pode usar o parâmetro "where" para especificar um registro usado em uma tabela. Isto é muito útil se você quer usar uma tabela única para seus identificadores, com registros diferentes de cada tabela.

seqhilo - Usa um algoritmo de hi/lo para gerar de forma bem eficiente qualquer tipo inteiro, dada uma SEQUENCE criada no Banco de Dados.

uuid.hex - Usa System.Guid e seu método ToString para gerar identificadores do tipo String. O tamanho da String depende do formato selecionado.

uuid.string - Usa um novo System.Guid para criar um tipo byte[] que é convertido para String.

guid - Usa um novo System.Guid como identificador.

guid.comb - Usa o algoritmo para gerar um novo System.Guid.

native - captura tipos identity, sequence ou hilo dependendo das capacidade existentes no Banco de Dados.

assigned - Deixa a aplicação decidir por um identificador para o objeto antes de Save() ser chamado.

foreign - Usa o identificador de outro objeto associado. Normalmente usado em conjunção com uma associação com chave primária com a tag <one-to-one>

Para a Tag property, não é necessária muita informação, são as propriedades da Classe Carro que refletem os campos da Tabela.

Mapeamento por Anotação/Atributo

Todos nós já sabemos que o NHibernate precisa de mapeamentos efetuados através de arquivos XML para que possa colocar em memória o ORM para a persistência. Para facilitar este trabalho foi criado um Projeto desenvolvido inicialmente por John Morris e que depois passou às mãos de Pierre Henri Kuaté, mais conhecido como KPixel. Este projeto decora as Classes com atributos o que torna o arquivo XML desnecessário posto que o Projeto gera um Stream para a memória como o XML faria, este projeto se chamado NHibernate Attributes que utiliza o assmbly NHibernate.Mapping.Attribute. Um exemplo de Classe mapeada com o assembly NHibernate.Mapping.Attributes na Código Fonte 4.

Código Fonte 4. Classe “decorada” com atributos NHibernate
    [Serializable]
    [Class(Schema = "RevistaArtigo02", Table = "Carro")]
    public class Carro
   
    {
        #region atributos
       
        private Int32 id;
        private String modelo;
        private String motor;

        #endregion

        #region Propriedades
        [Id(Name="Id",Column="id"),Generator(1,Class="identity")]
        public virtual Int32 Id
        {
            get { return this.id; }
            set { this.id = value; }
        }

        [Property(Column="modelo")]
        public virtual String Modelo
        {
            get { return this.modelo; }
            set { this.modelo = value; }
        }

        [Property(Column="motor")]
        public virtual String Motor
        {
            get { return this.motor; }
            set { this.motor = value; }
        }

        #endregion

        #region Construtores

        public Carro()
        {

        }

        public Carro(String modelo, String motor)
        {
            this.Modelo = modelo;
            this.Motor = motor;
        }
        #endregion

        #region Sobrescritas
        public override string ToString()
        {
            return this.id + ", " + this.modelo + ", " + this.motor;
        }
        #endregion
    }
}

Arquivo de Configuração do NHibernate

O Nhibernate é um framework bastante versátil no qual permite que o configuremos via Código ou via arquivo XML, neste artigo vamos abordar o mapeamento via XML, para maiores informações sobre a inicialização do NHibernate via código,e só acessar o site http://nhforge.org/doc/nh/en/index.html#session-configuration. O Nhibernate precisa ser inicializado através da interface ISessionFactory, com ela, todas as Entidades são compiladas e colocadas na memória, assim como os comandos deste arquivo XML são aceitos e colocados em memória aguardando sua execução. Como padrão, o NHibernate aceita como arquivo de configuração contendo a extensão .cfg.xml, normalmente cria-se um arquivo na raiz do Projeto chamada hibernate.cfg.xml. Neste arquivo, registramos inclusive a string de conexão, dialeto envolvido e outras configurações de ambiente. Na Código Fonte 5, vemos um exemplo deste arquivo.

Código Fonte 5. Arquivo de configuração hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
  <session-factory>
    <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
      <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
   
      <!-- Aqui você deve coloca o seu Connectio String -->
      <property name="connection.connection_string">
        Data Source=ALLSPARK\SQLEXPRESS;Initial Catalog=RevistaArtigo02;Integrated Security=True
      </property>
   
      <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
    <property name="show_sql">true</property>
    <property name="current_session_context_class">thread</property>
  </session-factory>
</hibernate-configuration>

Diante desta informação, já podemos instanciar a nossa ISessionFactory, que funciona como uma fachada. O mais importante é que devemos sempre prezar pela performance, é necessário que criemos uma Classe que gere um Singleton para que o NHibernate possa ser carregado e controlado de forma independente e desacoplada do código, na Código Fonte 6, temos um exemplo de Classe que contém este padrão de Projeto. Esta Classe acessa o arquivo de configuração e faz com que toda a “mágica” do que fizemos até agora aconteça e que faça a conexão com o Banco de Dados

Código Fonte 6. Classe NHibernateUtil
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NHibernate;
using NHibernate.Cfg;

namespace DAL.Reuso
{
    public sealed class NHibernateUtil
    {
        #region Atributos

        private static readonly ISessionFactory fabricaDeSessao;
        private const String CodSessaoAtual = "nhibernate.current_session";

        #endregion

        #region Construtor

        static NHibernateUtil()
        {
            try
            {
                ISessionFactory fabrica = new Configuration().Configure().AddAssembly("DAL").BuildSessionFactory();
               
            }
            catch (Exception)
            {
               
                throw;
            }
        }

        #endregion

        #region Métodos de Controle

        public static ISession PegaSessaoEmUso()
        {
            ISession sessao = fabricaDeSessao.GetCurrentSession();

            if (sessao == null)
            {
                sessao = fabricaDeSessao.OpenSession();
            }

            return sessao; 
        }

        public static void FecharSessao()
        {
            ISession sessao = fabricaDeSessao.GetCurrentSession();

            if (sessao != null)
            {
                sessao.Close();
            }
        }

        public static void FecharFabricaDeSessao()
        {
            if (fabricaDeSessao != null || !fabricaDeSessao.IsClosed){
                fabricaDeSessao.Close();
            }
        }

        #endregion

    }
}

Esta Classe, o leitor pode já utilizá-la como está em seus projetos e até aprimorá-la mais para que fique mais genérica a seu uso, assim como em futuros artigos que faremos de forma mais prática demonstrando o uso de Generics, Fachadas e outros padrões de projeto que facilitem a implementação nos projetos de nossos leitores. Já criamos uma Classe que irá e muito facilitar o nosso trabalho de acesso a base de dados, diante disto, vamos mostrar a melhor forma de utilizá-la.

Acessando a Base de Dados através do NHibernate

Criamos na Código Fonte 6 uma Classe para acesso a Base de Dados, agora, veremos como que NHibernate se comunica com a Base de Dados para permitir que a persistência seja efetuada.
Inicialmente, como uma melhor prática, o NHibernate trabalha com transações, esta é uma forma bem segura de trabalho, posto que caso algo dê errado, um Rollback pode ser dado, caso contrário, um commit grava as alterações no Banco de Dados e a conexão é finalizada, salvo para operações de busca, onde a sessão continua em aberto. Devemos ter isso me mente até mesmo quando o projeto solicitar que utilizemos mais de uma Classe em uma transação. O Nhibernate abre uma transação, efetua o trabalho que precisa fazer, trata este trabalho, caso execute com sucesso, executa um commit, caso não executa um Rollback e posterimente fecha a sessão. Uma Sessão é composta de Transação, execução e Fechamento, o fechamento pode ser um finally com um comado de fechamento ou simplesmente uma execução de operações, neste caso, por exemplo, seria um INSERT, UPDATE e DELETE suficientes para fechar a Sessão o que também não seria necessário que a fechássemos, um fragmento deste trabalho, pode ser visto na Código Fonte 7 já com o uso de nossa Classe NHibernateUtil.

Código Fonte 7.NHibernate interagindo com o Banco de Dados
        public void Gravar(Carro carro)
        {
            ISession sessao = NHibernateUtil.PegaSessaoEmUso();
            ITransaction transacao = sessao.BeginTransaction();

            sessao.Save(carro);
            transacao.Commit();

            NHibernateUtil.FecharSessao();
        }

    }

Como sabemos, o Banco de Dados isola as operações através das transações, diante disso, temos um singleton abrindo e fechando a sessão de forma protegida.
Agora, é só criar um Projeto e implementar a solução. E isso é assunto para um outro artigo.

Conclusão

O NHibernate é mais uma opção frente aos outros frameworks ORM existentes no mercado, ele não perde em nada com relação ao Microsoft ADO.NET Entity Framework e outros. Vimos neste artigo o poder deste framework que já faz bastante sucesso no meio Java e que pode também fazer bastante sucesso no mundo .NET. Vimos apenas 20% de todo o framework e com esta informação, já podemos trabalhar em um Projeto de médio porte. Em artigos próximos, vamos falar de outras partes do NHibernate. Este por ser um framework um tanto extenso se fizéssemos aqui a cobertura deste, com certeza ficaria cansativo ao leitor.

Fortaleça sempre o trabalho em equipe porque sozinho nada se consegue neste mundo, é um prazer tê-lo como leitor.

Forte Abraço