Como eu Desenvolvo Software – Parte 2

Se da última vez que escrevi, eu achava que estava sobrecarregado, eu ainda não tinha idéia do que estava vindo! Estes últimos meses tem sido extremamente agitados na White Fox, estamos abrindo várias frentes, criando iniciativas onde um bom software possa ser um diferencial competitivo. Estamos tendo mais sucesso do que eu esperava inicialmente, este ano está sendo fechado com chave de ouro. Em breve, vou escrever sobre algumas destas inciativas aqui. Mas deixe-me continuar finalmente o post anterior, falando sobre desenvolvimento de software.

Como citei no post anterior, a ideia é tentar falar um pouco da maneira pela qual eu desenvolvo software, considerando as metodologias e correntes atualmente em voga. Para começar, acredito que o mais importante de todo o processo é entender que desenvolver software é entregar, dentro de um prazo e um custo acordado, algo que atenda uma necessidade de alguém de maneira satisfatória. Por mais óbvio que isto pareça, a maior parte dos desenvolvedores esquece estes objetivos primordiais. Acredito que isto aconteça porque, como desenvolvedores, tendemos a não lembrar que desenvolver software é uma atividade extremamente cara para quem compra. Assim, não cumprir um prazo pode significar uma perda de oportunidade que inviabiliza todo o investimento. E é claro que não cumprir custo pode fazer com que um investimento seja inviável em termos de resultado financeiro, determinando a falência do projeto. E mesmo atingindo ambos os objetivos, o software ainda tem que servir para melhorar algum processo ou trazer alguma receita, de forma que o investimento feito seja compensado. Claro que muitas vezes isto é difícil de ser avaliado, mas, por mais difícil que seja, isto é o que queremos obter quando desenvolvemos comercialmente. É importante ter isto em mente porque nossas decisões, como desenvolvedores ou arquitetos, vão justamente impactar estas características e elas só fazem sentido quando permitem que a construção do software seja financeiramente viável.

Entregar um software que funciona bem também parece ser algo óbvio, mas que na prática é justamente uma dos pontos mais subjetivos da nossa profissão. Por exemplo, na prática poderíamos construir praticamente qualquer sistema utilizando somente telas de CRUD (listar, editar, alterar, remover). Apesar de funcionar, um sistema assim muito provavelmente seria quase impossível de ser utilizado com eficiência pelos usuários – eu mesmo já tive a chance de conhecer vários sistemas feitos desta forma! No extremo oposto, o software pode ter poucas telas, extremamente complexas e que fazem “tudo”, o que podem pro um lado ser um pesadelo para quem usa ou, mesmo sendo fácil de usar, ser um pesadelo para manter e evoluir – novamente, já vi vários assim. Encontrar o ponto ideal de usabilidade versus produtividade é pra mim um dos maiores desafios do desenvolvimento. No entanto este é um assunto que eu raramente vejo em fóruns técnicos.

Um outro ponto importante dos conceitos gerais de desenvolvimento é a questão da manutenabilidade de software. Esperamos que o software que construímos seja usado por muito tempo, talvez até muitos anos. Assim, se um software for extremamente caro para manter (e manter aqui significa alterar e evoluir o mesmo conforme necessário para o negócio), ele também pode se tornar inviável para quem está comprando. Daí toda esta preocupação de desenvolvedores em fazer software utilizando uma estrutura que permita que ele seja entendido e alterado com facilidade por alguém no futuro. O problema é que isto é também é algo extremamente subjetivo, já que é difícil determinar qual o grau de estruturação que vai permitir atingir este custo ideal de manutenção. Em outras palavras, é muito complicado determinar o quanto a mais tem-se que gastar no desenvolvimento e calcular em quanto isto deixa a manutenção mais barata. Alguns acham, por exemplo, que somente software que faça uso da última moda em BDD, TDD, DDD e etc. teria um custo de manutenção adequado. Mas muitas vezes, o que se gasta para se incluir uma determinada metodologia ou técnica em um processo de construção torna o software tão caro pra fazer que a conta não fecha. Por outro lado temos exemplos de software com anos de uso, em FORTRAN ou CLIPPER, que estão em uso até hoje e que possuem ainda um custo de manutenção que não justifica uma eventual migração. Claro que cada caso é um caso, porém, na minha visão, este é um dos conceitos que mais geram dúvida e que mais é erroneamente empregado por desenvolvedores em geral.

Pra mim a manutenabilidade é super crítica, já que o software que eu construo tem a tendência de ser usado por muito tempo – tenho alguns que estão em uso há mais de 10 anos. Ao longo dos anos, surgem e desaparecem metodologias e técnicas, plataformas e linguagens são evoluídas. A minha solução para isto foi sempre criar um framework que abstraia os artefatos produzidos, na medida do possível, de toda esta mudança, sem perder as novidades. Pra mim, de longe, o mais caro no software é construir interfaces. Assim, desde os tempos do ASP eu trabalho com algum tipo de abstração de interface para tornar isto um pouco mais barato – ver posts sobre produtividade. E interessante que, apesar de todas as mudanças, estes frameworks têm alguns pontos comuns. Claro que hoje em dia, com MVC e jQuery, a facilidade para abstração é muito maior. Porém algumas áreas como visualização e separação lógica, permanecem de maneira muito similar.

Com relação à usabilidade, eu tento manter a criação de telas complexas no limite do que o framework permite. Claro que com a evolução do framework, hoje é possível fazer praticamente qualquer tela. Mas ainda assim, eu sempre prefiro utilizar telas que sejam mais simples de expressar no framework, desde que não atrapalhe muito a usabilidade. É claro que é sempre possível fazer algo super complexo, porém deixo claro para quem compra que a escolha por uma interface complexa vai gerar um maior custo pra fazer e um muito maior pra manter, o que usualmente é um argumento bastante convincente. E interessante de ser ter software usado por muito tempo é que ao longo dos anos, os indicadores mostram que a maior parte do custo de evolução se concentra justamente nas telas complexas, confirmando a teoria de que telas grandes são algo bastante difícil de se justificar, financeiramente.

Então, retomando o tópico principal, como desenvolvo software? Tudo começa com uma necessidade. Para efeito de ilustração, vamos assumir daqui por diante uma necessidade de um cliente que deseja, por exemplo, passar a controlar dados que estavam numa planilha. A partir daqui vou tentar imaginar um software que atenda a necessidade, que seja feito utilizando os artefatos do framework e que custe algo pra fazer que possa ser retornado em algum tempo. Neste exemplo, de uma planilha, o ganho do cliente seria a redução da perda é de tempo dos usuários (afinal manter planilhas grandes é algo complicado), a diminuição do risco de um erro de digitação (planilhas grandes são difíceis de operar) e no aumento da disponibilidade da informação proporcionada por um sistema web.

No próximo post, continuarei deste ponto mostrando como uma necessidade é transformada em software e quais os critérios que uso na construção dos artefatos e na implantação final do produto. Prometo não demorar pra escrever a continuação!

, , , , ,

1 comentário

Como eu Desenvolvo Software – Parte 1

Olá pessoal. Acabei ficando um longo tempo sem escrever, tenho estado anormalmente ocupado nestes últimos meses. Junho foi um mês muito complicado, com as paradas para a Copa do Mundo e a presença no Agile Brazil – isto sem falar em alguns projetos críticos sendo entregues e o tempo dedicado a novos projetos. Com isto, na White Fox, passamos o mês bem sobrecarregados e só agora em Julho é que começamos a voltar ao ritmo normal. Finalmente agora volto a ter um pouco de tempo para escrever alguns artigos!

O evento Agile Brazil foi muito bom. Contou com a presença de grandes nomes do desenvolvimento mundial, como o Martin Fowler e o Philippe Kruchten. E foi excelente pra fazer contato com o pessoal da comunidade .NET do Brasil. Mas este post é sobre algo que conversei bastante com outras pessoas lá e que gera sempre discussões “acaloradas” toda vez que é levantado em uma roda de desnvolvedores (ou em listas de desenvolvedores como a excelente dotnetarchitects): qual o melhor “jeito” de desenvolver software.

Com a aceitação cada vez maior das metodologias ágeis, a minha impressão era que a comunidade estaria mais tranquila sobre metodologias e técnicas para desenvolvimento. Elas sempre evoluem, claro, mas pelo menos em linha gerais, estaríamos em um caminho mais definido. Mas o que eu vejo é que ocorreu algo diferente, uma profusão de metodologias e técnicas e de pessoas que pregam que determinada linha é muito melhor do que qualquer outra. Acho que isto piorou ainda mais porque o modelo “waterfall”, hoje praticamente execrado pela maior parte dos desenvolvedores “modernos”, foi pintando como o maior vilão e origem de todos os males e que a metologia XXX ou YYY chegou para resolver isto!

Longe de mim defenter o uso de waterfall, na maior parte dos cenários. Mas, apesar de todos os problemas e falhas deste tipo de desenvolvimento, o fato é que até hoje temos muitos e muitos exemplos de sucesso,  e que continuam a acontecer ainda hoje. A palestra do Kruchetn foi muito boa neste aspecto, ele citou justamente esta “demonização” do waterfall como um sinal de que nem tudo está bem.

E para quem está começando, eu tenho notado, em geral, uma total confusão. Como as universidades estão alguns ciclos atrás (a maior parte delas ainda prega o waterfall como única e melhor maneira de desenvolver) e sem ter a experiência de ter passado por sucesso e falhas em determinada metologias, os iniciantes ficam sem saber que caminho seguir: Waterfall? nem pensar! Scrum? Como viver com todos os scrum-buts? XP? Como aplicar no meu dia a dia? Isto sem falar nas múltiplas variantes técnicas da construção em si, onde entra TDD, BDD, DDD etc., aliada a uma miríade de plataformas, frameworks, ORMs etc. E sem nem entrar no mérito de linguagens em si, onde temos novamente uma outra gama de escolhas como C#, C++, Java, Rubi, Python etc. E em todos estes, temos muitos “gurus” que afirmam que aquilo é o supra-sumo da agilidade e é a melhor invenção do mundo deste a pólvora, sendo a única maneira de fazer software bem. Realmente é difícil não ficar confuso. Felizmente temos algumas vozes de bom senso, como o Alistair Cockburn, no seu “Juramento de Não-Lealdade”, que talvez comecem a melhorar este cenário.

Na minha visão, escolher uma forma de desenvolver não deveria causar tanta dificuldade. É certo, desenvolver software é algo complicado, exige um misto de dedicação, talento, experiência e muito, muito estudo. Mas, em toda a minha experiência, o que eu tenho visto é que as pessoas que se eforçam para se tornar bons codificadores acabam o sendo de uma maneira geral. Ou seja, um bom programador em C# vai ser um bom programador em Python (claro que uma transição não é simples, pelo volume de novas informações, mas se isto for desejado ou necessário, vai acontecer). Ou seja, os princípios básicos, o cuidado e o gosto pelo que se faz, são fatores mais importantes no sucesso do que qualquer linguagem metodologia, tecnologia ou plataforma que exista. E pra mim, isto vai continuar sendo assim mesmo com o todo o nosso ritmo de evolução… (acho que só vai parar o dia que pudermos solicitar um programa verbalmente e o próprio computador  o gerar… ou seja, daqui a muito tempo!). Óbvio também que nada existe sem contexto, isto é especialmente válido para desenvolvimento de software. Assim, levando em consideração o contexto, algumas escolhas podem levar a uma maior produtividade do que outras.

Para tentar ajudar nestes dilemas, resolvi escrever este e mais um artigo sobre desenvolvimento, e como eu o vejo. Vou colocar alguns princípios que adoto, na medida do possível, de onde eles vieram, e como eu vejo a inserção de toda esta parafernália metodológica/técnica no meu processo de desenvolvimento. Meu objetivo é tentar defender a idéia mais importante dos princípios ágeis, de que pessoas são mais importantes do que qualquer processo, metodologia ou ferramenta, e que os principios básicos ainda são comuns a qualquer atividade de desenvolvimento. Vou também colocar um pouco do meu contexto, para justificar algumas escolhas que faço e como isto afeta a nossa produtividade.

Até a próxima!

, , , , , ,

3 Comentários

Manutenção de Sistemas

Hoje passei grande parte do dia conversando com clientes e parceiros sobre o processo de desenvolvimento em sistemas onde a maior parte do código é legado. Feliz ou infelizmente este é o caso da White Fox, onde o maior volume de esforço é feito em sistemas que já possuem muitos (alguns mais de 10) anos de existência (vieram por herança da empresa de onde a White Fox foi spin-off). Se por um lado isto demonstra um certo grau de satisfação do cliente por ter mantido o desenvolvimento conosco este tempo todo; por outro significa uma maior complexidade para manter e evoluir o sistemas.

Um ponto interessante que muitas pessoas parecem não compreender é que um sistema “envelhece”. Um argumento comum é “se funcionou até hoje, porque iria parar de funcionar?”. O que este argumento não leva em consideração é que todo sistema tem partes mutáveis. Sejam os dados que mudam de forma ou de volume, seja a plataforma que tem que ser evoluída por obsolescência de hardware ou software. E quanto menos se mexe, mais este processo é visível; tivemos um caso recente em que vulnerabilidades no IIS 6.0 fizeram-nos mudar para o IIS 7.0, porém componentes feitos para rodar no IIS antigo não funcionavam mais no novo e para completar, o fabricante do componente nem existia mais, o que impedia o seu simples upgrade. Acho que a comparação é como a da manutenção de um carro: é possível rodar sem manutenção até um ponto onde componentes começam a parar ou apresentar defeito…. Mas quanto mais se demora para se fazer as manutenções iniciais, mais caro vai ser quando elas se tornarem inevitáveis.

Outro ponto interessante comumente visto é a concepção errônea de que um modelo waterfall seria menos propenso a erros em um processo de manutenção. Definitivamente não é verdade, o que um modelo waterfall normalmente define é um tempo alocado explicitamente para planejamento de migração de legado, testes e garantia. Porém, isto acontece a um custo bem mais elevado do que em uma metodologia ágil. Em uma metodologia ágil também seria possível planejar e gerar testes caso isto traga valor para o cliente. A único questão é quantificar este valor para que a relação custo/benefício faça sentido e estas tarefas sejam adequadamente priorizadas, de forma tão ou mais importante do que as novas funcionalidades.

E interessante que a maior parte da literatura para o desenvolvimento de software trate de novos projetos, existindo pouca coisa específica para o processo de manutenção. Metodologias como o Scrum nem sequer se aplicam a processos de manutenção. Isto me deixa intrigado pois, a meu ver, a maior parte do esforço mundial de desenvolvimento deveria ser em manutenção; não faz sentido nem é sempre economicamente viável reescrever um sistema do zero a cada grande mudança. E nas metodologias ágeis há um dilema já logo no início, pois todas pregam a preparação de um sprint com aquelas tarefas que geram maior valor para o cliente. Ora, evoluir algo que já funciona dificilmente vai ter mais valor para o cliente do que qualquer nova funcionalidade, por menor que seja. Assim, evoluções acabam sempre ficando para segundo plano até o ponto onde acontece um problema grave com uma funcionalidade antiga e aí é um deus-nos-acuda para portar a mesma à toque de caixa ou achar um guru que ainda consiga mexer no legado de uma forma relativamente segura.

Há algum tempo atrás escrevi um post sobre a necessidade de refatoração de grandes sistemas. Hoje vou mais adiante, acho que além da refatoração, é necessário um esforço constante de migração e evolução dos sistemas e componentes legado. Isto deve ser feito enquanto o conhecimento de negócio e técnico ainda está disponível na empresa, para que o custo de atualização fique em um patamar viável. Neste aspecto, a existência de testes unitários e de integração são de grande valia, pois o tempo gasto na geração deste tipo de artefato acaba também se pagando na hora de uma eventual atividade de evolução. Toda a questão é como vender este tipo de atividade para um cliente. O que temos feito recentemente é tentar definir um percentual fixo das horas de desenvolvimento exclusivamente para serem gastas na evolução de partes antigas do sistema. Claro que estas tarefas também estão sujeitas a uma priorização entre si, usando como critério as funcionalidades que são mais críticas para o negócio do cliente e o grau de obsolescência das mesmas.

Outro ponto crítico na questão da manutenção é o procedimento de deploy. Em um sistema novo, fazer deploy é sempre algo trivial (acabamos de encerrar um sistema, por exemplo, onde o deploy é uma simples cópia de arquivos e uma pequena configuração do IIS; daqui a 3 anos duvido que este processo seja tão simples se a aplicação não for evoluída). E o esforço para se manter atualizado uma documentação de deploy para cada Service Pack ou alteração de infra-estrutura não é trivial e convencer o cliente de que isto é algo prioritário e que deve ser feito durante algum sprint é também algo extremamente complicado. De novo o grande motivador é a ocorrência de um erro mais grave, porém aí tudo já vai ser feito sob uma considerável quantidade de stress.

Ainda não sei qual é a melhor maneira de lidar com estas situações. Na White Fox estamos trabalhando com alguns clientes no sentido mitigar estes problemas e trabalhar ativamente na migração de legado. Os sistemas são tão grandes, no entanto, que é necessário um bom trabalho de priorização e um critério rígido de limites para que este esforço não consuma todo o previsto de uma deterimada sprint. Estamos tendo sucesso em alguns casos, porém ainda temos muito a melhorar e acredito também que algumas ferramentas poderiam ser de grande valia na avaliação dos resultados de determinada linha de ação.

, , , ,

2 Comentários

White Fox: O Início

Após a definição do conceito do que buscava nesta minha nova empreitada, a White Fox está finalmente pronta e iniciou suas operações oficialmente este mês! É sempre algo muito especial participar da construção de uma empresa nova; mais especial ainda quando é uma empresa que representa um ideal buscado por muito tempo. Claro que iniciar uma empresa é sempre muito trabalho, muitos trâmites burocráticos, investimentos, implantação de toda a parte de infra-estrutura (ambiente, rede, telefonia etc.) e planejamento financeiro, tudo isto simultaneamente às atividades cotidianas e com uma equipe mínima! Muito cansativo, mas está valendo muito!

Estamos conseguindo começar com um time pequeno, mas totalmente alinhado ao conceito do que queremos para a empresa. Estamos também definindo os contratos de prestação de serviço com os clientes existentes para adaptá-los 100% a um formato ágil. Há uma grande necessidade de negociação, pois, por maior que seja a maturidade deles, sempre é complicado definir um contrato baseado neste tipo de desenvolvimento. Estamos tendo sucesso até agora e acredito que esta experiência vai tornar mais fácil a negociação com novos clientes.

Apesar de contarmos com uma grande vantagem inicial, que é a existência de clientes ativos, a White Fox vai agora encarar desafios bem importantes para se manter e crescer. Na minha visão, os problemas mais difíceis são a definição do processo de prospecção/venda mantendo o resultado, a gestão do crescimento e a definição de um processo interno de desenvolvimento. A questão da prospecção/venda é a mais difícil de ser equacionada. Prospectar significa ter tempo livre, fazer concessões a potenciais clientes (consultorias, análises, demonstrações) para que seja possível mostrar o nosso serviço e permitir que eventualmente seja fechado um contrato de desenvolvimento. Porém, este processo é caro já que o tempo investido é um tempo que deixa de ser aplicado no atendimento de clientes existentes e que, portanto, deixa de gerar receita. E um modelo agile prevê uma confiança enorme da contratante na contratada, o que faz com que muitas prospecções, para funcionar, exijam um grande investimento. Desta forma, prospectar pode significar comprometer grande parte, senão todo, o resultado obtido nos projetos em andamento para tentar trazer clientes novos. Assim, isto é algo que tem que ser pesado com muito cuidado e o que estamos fazendo é praticamente “escolhendo a dedo” os potenciais clientes que achamos que vale a pena prospectar. O lado negativo é que o crescimento é extremamente lento.

A gestão do crescimento é outro grande desafio. Encontrar profissionais prontos com o nível que necessitamos na White Fox é uma tarefa quase impossível. Encontrar pessoal com potencial é bem mais simples, porém treinar alguém com potencial até que esteja no ponto necessário é algo demorado e caro. E escolher o momento de incluir alguém também é algo que exige grande cuidado, já que para prospectar é necessário ter algum tempo livre de pessoas excelentes; no entanto a prospecção é lenta e o treinamento também, assim estes custos podem comprometer completamente o resultado de um período. Estamos tentando balancear isto com um programa de estágio para tentar identificar pessoas com grande potencial e ter um tempo de treinamento mais longo e com isto conseguir um tempo maior para prospecção.

O outro desafio que eu vejo é com relação à gestão interna do processo de desenvolvimento. Nós. por princípio temos uma equipe muito pequena, mas ainda assim é necessário um fluxo de trabalho que garanta o andamento das atividades básicas. Além disto, todos os clientes exigem algum tipo de contabilidade para a gestão de custos, o que significa que tudo o que fazemos deve ser de alguma maneira registrado e totalizado para efeito de faturamento. E como quase todos os nossos projetos são muito antigos, temos que funcionar bem com a manutenção e evolução de sistemas legados de qualidade variada. O que temos feito é dedicar um tempo na busca e definição de metodologia e ferramentas para nos apoiar nesta gestão. Gostamos muito do Scrum mas ele não é aplicável a projetos de manutenção. Da mesma forma, a maior parte das ferramentas de gestão de processos ágeis existentes deixa muito a desejar nesta parte de manutenção e contabilização. Estamos avaliando várias e considerando também a possibilidade de desenvolvimento de uma nossa, simples, mas quer iria atender por completo as nossas necessidades. Acredito que nas próximas semanas nós iremos chegar a uma definição.

No mais, muito trabalho, mas com uma equipe muito motivada. Estamos adotando uma postura minimalista com relação à parte administrativa e financeira; terceirizando quase tudo e adotando práticas que simplifiquem isto ao máximo. O mesmo vale para a parte de infra-estrutura, estamos utilizando quase tudo na Cloud; a única exceção é o source-control, que ainda assim fica em um espaço contratado de um datacenter. Conforme fomos evoluindo vou postando aqui as soluções e caminhos que adotamos. Até a próxima!

, , , , ,

4 Comentários

Instalando o TFS 2010

Esta semana, para aproveitar a mudança da sede da WhiteFox, resolvemos já também migrar o nosso Microsoft Team Foundation Server (TFS) de 2008 para 2010. Como toda instalação de TFS, estávamos bem preocupados, já que no 2008 uma instalação completa era sempre difícil de funcionar da primeira vez. No TFS 2010 não foi diferente, foram praticamente dois dias de trabalho de duas pessoas (dev e infra) para conseguir colocar tudo funcionando! Para que outros não tenham que passar por esta mesma sina, seguem abaixo algumas dicas e experiências que aprendemos na “marra”.

Primeiramente, resista à idéia de “vamos instalando” pra ver no que dá. Cada instalação de componentes do Windows 2008 e SQL Server deve ser feita de uma maneira específica ou então erros estranhíssimos acontecem. Tentamos desta forma inicialmente e obviamente, não funcionou. O TFS Installation Guide é, portanto, leitura obrigatória. Mas atenção que a versão que vem no DVD NÃO é a mais atualizada; é necessário baixar a mais atual da MSDN. O link para download está na primeira página do próprio Installation Guide. A versão mais atualizada é bem mais detalhada, com os passos mais específicos para cada item. Siga à risca! A única coisa que não seguimos foi com relação as permissões da conta TfsService. No Guide eles recomendam não dar privilégio administrativos; somente alguns específicos e “logon as a service”. Como esta parte era uma fonte enorme de dor de cabeça no TFS 2008, resolvemos ignorar e colocar esta conta como Domain Admin.

Um ponto que gerou uma dúvida seria se o TFS 2010 pode ser executado no Windows Server 2008 R2. Os pré-requisitos diziam que sim, mas eu encontrei alguns blogs afirmando que o suporte no R2 é possível, mas mais complicado. Acabou que confirmamos que de fato é possível, só não está claro no Installation Guide (a impressão que dá é que o Guide foi feito para o 2008, não para o 2008 R2) que o SQL Server 2008 não é compatível com o Windows 2008 R2, assim, imediatamente após a instalação do SQL Server, é necessário instalar o Service Pack 1.

Agora o ponto mais crítico que encontramos (e este nos fez perder quase 1 dia para descobrir o que era) é que, como toda empresa de desenvolvimento parceira Microsoft, nós utilizamos o SQL Server 2008 Developer Edition como banco de dados. Porém, a instalação desta versão tem uma característica única: ela por default não habilita o acesso TCP/IP. Sem este acesso, ocorre um erro na hora do installer do TFS criar o suporte ao ReportServer. E o erro é uma daquelas mensagens 0x8XXXX que não dizem absolutamente nada. Identificar este erro não foi simples, felizmente encontramos alguns blogs que acabaram nos apontando o caminho correto (isto depois de várias reinstalações frustradas). A correção, no entanto, é trivial: basta ativar o TCP/IP no SQL Server Configuration, imediatamente após a instalação do mesmo.

Com isto, tudo finalmente funcionou. O processo completo de instalação do Windows 2008 R2 + Sql Server 2008 + Sql Server 2008 SP1 + TFS demorou cerca de 2,5h no nosso hardware (que não é muito potente).

Após a instalação, nos deparamos com a nova feature do TFS 2010, que são as Project Collections. Ele cria uma por default na instalação, a “DefaultCollection”. No nosso caso, como temos vários clientes, resolvemos apagar esta e criar uma para cada cliente. Para apagar uma Project Collection, é necessário utilizar o “TfsConfig.exe”, que fica no diretório Tools de instalação do TFS 2010.

Ainda estamos analisando o uso das Project Collection. No Team Explorer, quando se abre uma Collection, ele fecha a anterior. Isto pode ser um problema para gente, já que temos projetos de infra-estrutura que são reutilizados entre os clientes. Mas iremos tentar esta abordagem e vamos ver como fica, já que ela aparentemente faz sentido.

Um último ponto diz respeito à segurança de acesso de collections criadas. No TFS Administrator, existe uma opção chamada “Group Membership”. Ali é possível dar permissão para outros usuários após a criação do mesmo. Porém, o problema do TFS 2008 permanece no TFS 2010, ou seja, dar permissão ali não significa acesso automático ao Sharepoint ou ReportServer. Assim, lembre-se sempre de dar permissões nestes dois itens após criar cada Collection. No ReportServer, isto pode ser feito na configuração raiz, vale para todas as Collections. No Sharepoint, é necessário fazer para cada site após a criação da respectiva Collection.

É isto pessoal, espero ter contribuído para tornar este processo um pouco menos penoso para outros. Por favor comentem caso tenham notado outros pontos ou se eu deixei alguma coisa relevante sem ser mencionada. Até a próxima.

, , ,

7 Comentários

Engine MVC baseada em XSLT

Na série sobre produtividade, no post sobre a camada de interface, eu  falei um pouco sobre o uso de XSLT na transformação de arquivos XML para gerar HTML. Várias pessoas me pediram mais detalhes sobre a engine utilizada. Neste artigo, vou entrar em mais detalhes desta engine usando o MS-MVC e fazer um paralelo com a implementação Monorail. Este artigo é bem técnico e pressupõe que o MS-MVC seja bem conhecido, em especial na parte de criação de custom view engines.

No download do MS-MVC existe uma View Engine baseada em XSLT. Existem também outras iniciativas como a Chris Hampson. Estas não nos atenderam porque se baseiam em um arquivo XML que é transformado diretamente pelo XSLT. No nosso caso é necessário injetar, além do arquivo XML de definição, dados que serão utilizados também pelo XSLT pra construir a tela. Finalmente, as atividades realizadas pelo controller também interferem na página resultante. Por exemplo, se uma ação gera um erro, a página resultante deve ser um redirecionamento para a página de tratamento de erros. Estas razões fizeram com que a gente desenvolvesse a nossa própria ViewEngine.

Em linhas gerais, a nossa ViewEngine simplesmente obtém o XML de definição, o XML de dados (que chamamos de DataIsland) e aplica um XSLT para gerar um HTML resultante (ver o post sobre camada de interface para exemplos destes artefatos). Para fazer isto no MS-MVC, é necessário implementar duas classes: uma que implemente a interface IViewEngine e uma que implemente a IView. Além destas duas, a implementação de nossa engine usa um controller base, para que possamos interceptar alguns métodos (ver adiante).

A IViewEngine tem que implementar os métodos FindView, ReleaseView e o FindPartialView. No nosso caso, a única coisa importante é o FindView. Este método é responsável por identificar o arquivo de definição XML e passar o tipo de ação e dados de apoio para a ViewBase, conforme código a seguir:

private ViewEngineResult FindView(ControllerContext controllerContext) {
            
            var server = controllerContext.HttpContext.Server;
            const string extension = "html";
            var area = string.Empty;

            if (controllerContext.RouteData.Values.ContainsKey("area")) 
                area = controllerContext.RouteData.Values["area"] + "/";

            var controller = (BaseController)controllerContext.Controller;
            var controllerName = 
                controller.GetType().Name.Replace("Controller", "")
                   .ToLowerInvariant();
            var path = string.Empty;
            if (controller.OutputType == OutputType.Html || 
                controller.OutputType == OutputType.XmlView) {
                if (controller.ViewNameOrigin == 
                        BaseController.ViewNameOriginType.Controller 
                    && string.IsNullOrEmpty(controller.ViewName))
                    path += string.Format("~/views/{0}{1}.{2}", area, 
                            controllerName, extension);
                else
                    path += string.Format("~/views/{0}{1}/{2}.{3}", area, 
                        controllerName, controller.ViewName ?? 
                        controller.ActionName, extension);
                if (!File.Exists(server.MapPath(path)))
                    return new ViewEngineResult(new[] { path });
                path = server.MapPath(path);
            }

            var dataIsland = 
                  (controller.OutputType != OutputType.XmlAction) ? 
                                controller.GetDataIsland() : null;
            var view = new XsltView(controller.OutputType, 
                controllerContext.RouteData.Values["area"].ToString(), 
                path, controllerName, controller.ActionName,  
                dataIsland == null ? null : dataIsland.Root, 
                controller.ElementsToRender, controller.Message, 
                controller.RedirectUrl);          
            return new ViewEngineResult(view, this);
        }

 

Como pode ser visto acima, temos dois casos de arquivos de definição. Alguns que tem o próprio nome do controller, e alguns que tem o nome da action, situados em um folder com o nome do controller. A última parte deste método chama o construtor da XsltView, que implementa a IView.

A XsltView é que contém o código principal da ViwEngine. Ela é responsável por efetivamente fazer a transformação XSLT e depois fazer escrever o HTML gerado. A transformação no nosso caso é um pouco mais complexa do que simplesmente rodar o Transform() de uma classe XsltCompiledTransform do C#. A gente faz também uma série de manipulações no XML de origem, fazendo a junção dele com o XML do DataIsland, alterando paths relativos (por exemplo, substituindo string ‘~/’ pelo path físico da aplicação) e suportando áreas que possuem XSLTs diferentes (por exemplo, temos áreas que simplesmente injetam HTML final; temos outras que usam pequenos templates XSLTs para cada controle; e outras que simplesmente fazem referência a um XSLT externo). Esta transformação poderia ser tema de um artigo por si só, quem tiver interesse em saber mais sobre ela, envie-me um email direto.

O método Render da XsltView é mostrado a seguir. Como mencionei acima, dependendo da ação executda nós podemos ter vários resultados possíveis. Temos HTML simples e, para chamadas que foram feitas por AJAX, temos opção de mandar mensagens de alerta, trechos de HTML para serem substituídos ou simples comandos de Redirect. Para suportar isto, fizemos uma mensagem XML que é decodificada por um arquivo .js que é responsável por tomar a decisão correta do que fazer com base nos elementos enviados.

public void Render(ViewContext viewContext, TextWriter writer) {
  XElement result;
  if (outputType == OutputType.Html) {
      var ns = GetNamespaces(contents.Document);
      if (!String.IsNullOrEmpty(redirectUrl)) {
       writer.Write("<html><script language='javascript'>location.href='" 
           + redirectUrl + "';</script></html>");
       return;
      }
      else {
       result = contents.XPathSelectElement(HtmlElementPath, ns);
       writer.Write(@"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 
 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>");
      }
  }
  else {
      viewContext.HttpContext.Response.ContentType = "text/xml";
      result = new XElement("Result");                
      if (!string.IsNullOrEmpty(message))
        result.Add(new XElement("Message", message));       
        if (!String.IsNullOrEmpty(redirectUrl)) {
           result.Add(new XAttribute("status", 
                       hasView ? "redirect" : "ok"));
           result.Add(new XElement("Redirect", redirectUrl));
        }
        else {                    
           switch (outputType) {
             case OutputType.XmlAction:
                result.Add(new XAttribute("status",  "ok"));
                break;
             case OutputType.XmlMessage:
                result.Add(new XAttribute("status",  "message"));
                break;
             case OutputType.XmlDataOnly:
                result.Add(new XAttribute("status", "ok"));
                result.Add(contents);
                break;
             case OutputType.XmlView:
                result.Add(new XAttribute("status", "ok"));
                if (elementsToRender != null) {
                   foreach (var element in elementsToRender) {
                      var ns = GetNamespaces(contents.Document);
                      var idName = element;
                      if (element == "Form" || element == "List"
                            || element == "CRUDList") {
                         idName = element + "Contents";
                         result.Add(
                           new XAttribute("configurePage", "true"));
                      }
                      var dataContents =
                      contents.XPathSelectElement(
                      String.Format("//*[name()='div' and @id='{0}']",
                             idName), ns);
                      if (dataContents == null) continue;
                      var item = new XElement("Data");
                      item.Add(new XElement("Id", idName));
                      var html = new XElement("Contents");
                      html.Add(new XCData(dataContents.ToString()));
                      item.Add(html);
                      result.Add(item);
                   }
                }
                break;
          }
     }
   }
   writer.Write(result.ToString());
}

O último componente da engine é o BaseController. Este controller faz o override de dois métodos do MS-MVC, o primeiro é o OnActionExecuting e OnActionExecuted, mostrados a seguir.

protected override void OnActionExecuting(
                                     ActionExecutingContext context) {
    ActionName = 
             context.ActionDescriptor.ActionName.ToLowerInvariant();
    if (HasAttribute<XmlResultAttribute>(context.ActionDescriptor)) {
        OutputType = OutputType.XmlView;
        var viewName = GetViewName(context.ActionDescriptor);
        if (!string.IsNullOrEmpty(viewName)) ViewName = viewName;
    }
    else 
        if (HasAttribute<ActionResultAttribute>(
                                   context.ActionDescriptor)) {
            OutputType = OutputType.XmlAction;
        }
        else
            if (HasAttribute<DataOnlyResultAttribute>(
                                    context.ActionDescriptor))
                OutputType = OutputType.XmlDataOnly;
            else {
                if (HasAttribute <HtmlResultAttribute>(
                                       context.ActionDescriptor)) {
                    var viewName =
                             GetViewName(context.ActionDescriptor);
                    if (!string.IsNullOrEmpty(viewName)) 
                              ViewName = viewName;
                }
                OutputType = OutputType.Html;
            }
    base.OnActionExecuting(context);
}

protected override void OnActionExecuted(ActionExecutedContext context) {
    if (context.Exception != null && !context.ExceptionHandled) {
        if (OutputType != OutputType.Html) {
            ProcessException(context.Exception);
            context.ExceptionHandled = true;
        }
    }
    base.OnActionExecuted(context);
}

Como pode ser visto, antes da execução, através de atributos da action, é possível determinar qual o tipo de retorno desejado. E após a execução, caso tenha ocorrido alguma exceção, é feito um tratamento da mesma.

Acho que deu para ter uma idéia de como a nossa engine foi construída. No Monorail, tudo funciona exatamente da mesma forma, a única diferença são nomes diferentes para interfaces e métodos do controller. No Monorail ao invés da IViewEngine, temos que herdar da ViewEngineBase. Não há interface específica para a View pois quem faz o Render é a própria ViewEngineBase. E no controller, o método a sofrer override é o InvokeMethod. Se alguém desejar informações específicas sobre o Monorail ou sobre qualquer outra área desta engine, basta me mandar um email.

, , , , , ,

1 comentário

Log de Negócio

Esta semana tive uma necessidade de log específica em um de nossos sistemas, que acabou gerando uma excelente adição para o nosso framework de desenvolvimento. A necessidade era saber o que acontecia em produção que acabava gerando um bug raro de valores no final do dia, e que fomos incapazes de reproduzir mesmo com uma grande quantidade de testes unitários. Aparentemente o bug está associado a uma condição de multi-usuário que é muito complicada de reproduzir. Para resolver este bug precisaríamos saber a seqüência exata em que as operações foram realizadas, e isto só seria possível com log.

A necessidade de log de negócio acaba surgindo vez por outra em aplicações. E, praticamente para cada sistema que eu já participei, foi feita uma solução diferente para log, com vantagens e desvantagens. Alguns usam log em banco, colocando triggers nas tabelas e replicando as informações, o que é extremamente simples de fazer mas gera grandes problemas de contenção e espaço utilizado. Outros usam versões do log4net gerando dados em arquivos, o que também é relativamente simples de fazer porém gera também dificuldade em ambientes massivamente multi-usuário – sem falar na dificuldade de processar os arquivos depois. Já fiz também log em banco, com e sem log4net, com bons resultados, mas sempre com uma solução específica para cada caso, o que causava um bom esforço de manutenção.

Outro ponto que sempre me incomodou é a questão da intrusividade das funções de log na regra de negócio. Tirando a solução em trigger de banco, qualquer implementação de log4net foi sempre feita com chamadas específicas de log dentro da rotina de negócio. Isto polui a rotina e dificulta a manutenção. Eu sempre tentei criar algo tipo AOP, mas nunca consegui uma solução boa. Mesmo atualmente, as bibliotecas conhecidas de AOP (PostSharp, Spring.Net etc.) não são uma boa opção para o nosso ambiente por serem um framework completo, de grande impacto…. Usar qualquer uma delas só pra log de algumas regras de negócio pra mim seria usar um canhão pra matar uma mosca.

Assim, resolvemos desta vez fazer algo decente, já incorporado ao framework, e que usasse uma abordagem AOP na medida do possível. Graças à tecnologia do Castle Windsor IoC usada atualmente no framework, isto foi relativamente simples de ser feito. Definimos na nossa arquitetura que o log de uma regra deveria ser expresso através da observação de parâmetros e retornos da função, antes e depois dela ser executada. Através do uso destas condições pré e pós, é possível identificar exatamente como aquela função afetou os dados.

Para implementar, o que fizemos foi usar o log4net para salvar eventos em banco. Até aí, solução padrão. O passo seguinte foi definir atributos AOP para marcar uma regra de negócio como sendo logada. O ideal teria sido definir as condições pré e pós no próprio atributo, porém o C# atualmente não suporta nada que não seja estático nos atributos; e usar strings eu acho uma prática ruim. A solução usada foi definir um tipo para o atributo que apontasse para uma classe que definiria quais são todas as condições, utilizando lambda.

O resultado ficou excelente. Abaixo está um exemplo do atributo na regra de negócio:

[Log(typeof(AtualizarValorDePosicaoLog))]
public void AtualizarValorAnterioDePosicao(Contrato contrato, 
                                    Posicao posicao, decimal valor) {
 

E no tipo de definição das condições, fica assim:

public class AtualizarValorDePosicaoLog : LoggerBase {

  public AtualizarValorDePosicaoLog() : base(
    Properties.PreCondition("valor"),
    Properties.PreCondition<Contrato>(
      c => c.Id, c => c.Tipo, c => c.TipoBacen, 
      c => c.Moeda, c => c.Posto, c => c.ValorMoedaEstrangeira, 
      c => c.ValorMoedaNacional, c => c.ValorUSD, c => c.TipoPagamento),
    Properties.PreCondition<Posicao>(
      p => p.Id, p => p.ValorPosicaoAnterior, p => p.Conformidade, 
      p => p.Baixa, p => p.Transferencias, 
      p => p.TotalCompra, p => p.TotalVenda, p => p.Moeda, p => p.Posto, 
      p => p.Tipo),

    Properties.PostCondition<Posicao>(
      p => p.Id, p => p.ValorPosicaoAnterior, p => p.Conformidade, 
      p => p.Baixa, p => p.Transferencias, 
      p => p.TotalCompra, p => p.TotalVenda, p => p.Moeda, p => p.Posto, 
      p => p.Tipo)) { }
    }
 

Na execução, o interceptor da classe de serviço busca o atributo, instancia o tipo de definição e coleta os dados, antes e depois da rotina executar. Para minimizar o impacto em tempo de execução, o logger simplesmente armazena um dicionário com todas as condições recolhidas, serializando num XML. Posteriormente, de maneira assíncrona e com baixa prioridade, um executor analisa os registros de log gerados e expande cada condição em tabelas do tipo dicionário.

Com isto o impacto ficou bem pequeno e conseguimos fazer algo quase puramente AOP e sem nenhuma definição em string! Finalmente conseguimos uma implementação boa para log, que deve nos atender por muito tempo.

Nesta solução, alem da equipe da WhiteFox, tivemos a colaboração do arquiteto Fernando Bichara, da Perlink.

, , , , ,

4 Comentários

Refatorando Grandes Sistemas

Olá, feliz 2010! Eu estou muito animado, as perspectivas para 2010 são muito boas! Fiquei agradavelmente surpreso com a reação das pessoas à White Fox, ela foi muito bem recebida e recebi várias mensagens de apoio, obrigado! Estou muito convicto de estar no caminho certo e construindo uma empresa que será um local excepcional para se trabalhar e prosperar. E o ano começa animado, este mês estamos entregando 2 primeiras versões de novos sistemas além de continuarmos com a manutenção dos existentes.

Conversando com um amigo na semana passada, caímos no assunto de grandes sistemas e do processo necessário à manutenção (evolutiva ou corretiva) para sistemas de grande porte em geral. O que me levou a refletir sobre isto pois, apesar de achar que processos e especialização por segmentos (silos) no desenvolvimento de software seja algo nocivo, não consegui achar nada de errado ou que eu faria diferente naquele caso específico.

O ponto é que grandes sistemas necessariamente exigem uma grande infra-estrutura. Por menor que seja uma mudança, para se garantir que um sistema não seja afetado por ela, é necessário avaliar todo o impacto, preparar uma sequência de testes específicos para a mudança e executar todo o conjunto de testes de integração e funcionais – muitos dos quais, até pelo volume, podem não ser totalmente automatizados. Isto sem falar no processo de liberação de versão em si, que envolve muitas vezes compilações e execuções de testes unitários que podem demorar várias horas. Em um time agile, mesmo com o máximo de automatização, este processo todo iria significar que o time iria passar a menor parte do tempo implementando a mudança e a maior parte do tempo, planejando, testando, integrando e verificando se tudo ficou ok. Não é algo viável, na minha visão. Acho que é por isto que o pessoal de Scrum afirma que Scrum é para novos projetos somente, não sendo adequado para manutenções.

Então o que fazer? Será que para grandes sistemas não é possível usar métodos ágeis? Ou que temos que assumir que para grandes sistemas, irão existir silos (testes e/ou gestão de configuração, por exemplo) e que isto é algo que não tem solução? Os que me conhecem sabem que esta resposta não é satisfatória pra mim. Porém, uma vez que o sistema tenha um certo tamanho, não consigo ver como fazer as coisas diferentes. Talvez então a saída seja não deixar os sistemas passarem de um tamanho crítico.

Claro que certas áreas exigem software de grande porte. O caso de um sistema operacional como o Windows, um aplicativo como o SAP ou mesmo um aplicativo como o Word, são sistemas que irão exigir toda uma infra-estrutura pesada para permitir manutenção e evolução. Não é à toa que o processo de testes da Microsoft, por exemplo, é algo extenso e complicado. Porém, não acho que isto deva ser usado para software de menor porte. Aliás, acho justamente o contrário, usar este tipo de técnica/processo para software de menor porte significa aumentar em ordens de grandeza o custo para desenvolvimento/manutenção e dificultar significativamente o uso de metodologias ágeis.

Assim, quando possível, acho que devíamos buscar quebrar grandes sistemas em aplicações menores. Usando os mesmos princípios aplicados em refatoração de código (tais como responsabilidade única, baixo acoplamento etc.), poderíamos tentar quebrar os sistemas em módulos para que, apesar de funcionarem em conjunto, sejam entidades separadas. Isto seria possível com uma definição clara de fronteiras, com o apoio de interfaces de serviços (SOA) e com um isolamento de interfaces com o usuário. Áreas de negócio distintas poderiam ser separadas de maneira que o domínio de negócio fosse separado, fazendo com que mudanças em um determinado setor do domínio não afetasse os demais. E mudanças na interface de serviços poderiam ser feitas de maneira versionada e evolutiva, no formato tipicamente usado em SOA.

Claro que uma refatoração de sistema só é possível com a participação direta do cliente, já que isto significa por si só uma mudança em processos, modo de trabalho e até mesmo na relação comercial, já que diversos módulos podem também significar mais de um fornecedor. Mas acredito que se isto gerar uma economia significativa no custo de desenvolvimento, será algo que terá grandes chances de ser adotado pelo cliente. O que mais uma vez ressalta a importância da área de contato com o cliente deter também todo o conhecimento técnico para propor e justificar mudanças como esta. Uma área puramente comercial teria muitas dificuldades em entender e vender algo deste tipo. Felizmente este não é o caso da White Fox.

Na White Fox nós temos alguns sistemas que estão começando a entrar no estágio em que a manutenção está começando a ficar cara, talvez seja este o momento de se pensar em uma refatoração deste tipo. Irei tentar colocar algo assim em prática e vou postando os resultados aqui. Novamente, um feliz 2010 para todos!

, , ,

1 comentário

White Fox: O Conceito

Nestes mais de 15 anos de carreira, meu sonho sempre foi ter meu próprio negócio, uma empresa de desenvolvimento de software que fosse algo diferente e reconhecida pela qualidade do seu pessoal e de seus produtos. Nestes anos, eu tive algumas empresas, todas buscando atingir esta meta, porém, por várias razões, nenhuma delas chegou a atingir este ideal.

Sonhos não morrem, eles permanecem no inconsciente e em momentos propícios, voltam com toda a força. E mais uma vez, está chegando a hora de tentar de novo e atingir o meu ideal. Estou na fase de concepção de uma nova empresa, a White Fox Consultoria e Desenvolvimento de Software. Neste post quero falar um pouco sobre o que estou buscando, quais as motivações e que valores eu estou trazendo para esta nova empresa.

Desta vez acho que tenho grandes chances de atingir meu sonho. Em todos estes anos, eu tive a oportunidade de ver o que funciona e o que não funciona na gestão de uma empresa de desenvolvimento, tanto em aspectos de relação interpessoal quanto na parte de resultados e controle. Acredito que esta experiência, acumulada com muitos erros e acertos (quero crer que tenham sido mais acertos do que erros), é essencial para que a White Fox possa ter sucesso.

Uma das lições mais importantes que eu aprendi é que para que uma pequena empresa tenha sucesso, os seus sócios devem fazer mais do que gerir. Não tenho dúvida que administrar bem em todos os aspectos (especialmente o financeiro) é fundamental para o sucesso. Porém isto não é nem de longe o suficiente. Pois por melhor que seja, um bom gerente administrativo não é capaz de trazer aquilo que cada vez mais eu acho que seja o mais importante: a paixão pelo que se faz, a inspiração para si e para seu time de fazer o melhor e buscar o melhor sempre, o orgulho e o prazer de se estar fazendo algo excepcional. E tudo isto só é possível quando o líder consegue ser a representação disto para todo o time; desta forma ele tem que ser (ou pelo menos ter sido) antes de tudo, um excelente técnico.

Assim, a White Fox está sendo concebida com o melhor de todos estes mundos. A paixão por desenvolvimento e tecnologia é o que move cada integrante da nova empresa. A gestão transparente e minimalista garante que todo o esforço seja voltado para o negócio da empresa, que é fazer bom software para seus clientes. E um time ultra-selecionado, com os melhores profissionais da área produz a mistura que irá permitir que ela tenha muito sucesso.

A White Fox traz alguns conceitos pouco usuais na nossa área. Um deles é que o time seja realmente formado por somente arquitetos/seniores, do tipo que é capaz de fazer um sistema do zero em pouco tempo e que é capaz de atuar em todos os aspectos de um sistema, do banco de dados à interface, passando pela gestão de atividades e pela interação com o cliente. A excelência técnica e o pragmatismo são as características mais relevantes dos membros do time da White Fox. Outro conceito é que cada integrante é um sócio na empresa, sendo responsável pelas decisões da sua alçada e recebendo uma parcela do resultado geral do negócio. Finalmente, iremos buscar um ambiente de trabalho extremamente agradável, sem nenhum tipo de burocracia desnecessária. A idéia é fazer da White Fox o local ideal de trabalho para um desenvolvedor.

O formato de desenvolvimento é o agile. Fortemente baseado no Scrum, mas sem ser Scrum, a empresa irá ter como missão produzir com software com ROI extremamente vantajoso para os seus clientes. O formato implica também uma certa maturidade do cliente. Contratar no formato agile significar que o cliente é também parte do desenvolvimento e o sucesso de qualquer projeto é diretamente proporcional à qualidade e envolvimento do pessoal do cliente. Claro que este formato não é para todos, mas para os que se encaixarem neste perfil, a recompensa será software sendo desenvolvido de maneira rápida e com um ROI excepcional.

Outro diferencial é emprego de um framework de desenvolvimento em todos os produtos construídos. Este framework tem sido evoluído ao longo de vários anos e agora está maduro o suficiente para permitir o desenvolvimento incrivelmente rápido de sistemas sem nenhum sacrifício de usabilidade ou manutenabilidade. O uso e padronização do framework no desenvolvimento da White Fox facilita também o treinamento de pessoal e a manutenção de todos os sistemas produzidos. E, pela qualidade das pessoas do time, o framework vai sendo evoluído a todo tempo para permitir ainda mais velocidade e qualidade final.

A empresa será lançada oficialmente no início do ano que vem. Extra oficialmente ela já está a todo vapor, o time inicial está definido e ela já tem faturamento suficiente para garantir o início das operações. O web site está sendo construído para representar todos estes valores e os clientes estão sendo apresentados à esta nova forma de trabalho (com muito sucesso até agora!). Antes que perguntem sobre o nome, Fox é uma palavra de relevância especial para mim, ligada à agilidade que quero representar. A cor branca transmite simplicidade, clareza e transparência. O logo final está abaixo:

 

logowhitefox

 

Se você se identificou com este sonho e quiser fazer parte deste empreendimento, me mande um email. O time inicial está pronto, mas a perspectiva é de crescimento rápido. Aguardem então para mais notícias sobre o andamento desta nova empresa!

, , , , ,

3 Comentários

Repositórios e Expressões Lambda

Esta semana fizemos mais uma grande melhoria na nossa camada de domínio, incorporando expressões lambda .Net nas nossas consultas e parâmetros de ordenação de repositórios. Retomando o post sobre a camada de domínio, os repositórios são utilizados para tirar a dependência da aplicação dos detalhes de persistência de objetos e para ser uma representação abstrata de uma coleção de entidades de domínio.

A idéia é que eles facilitem a busca de entidades específicas ou implementem métodos mais complexos para a execução de consultas. Como no nosso caso usamos o NHibernate para camada de persistência, nós temos duas opções de efetuar estas consultas, os Criterias (DetachedCriteria ou expressões ICriterion simples) ou o HQL, que é uma linguagem de consulta parecida com SQL só que composta pelas entidades de domínio.

Consultas HQL são baseadas em strings (ver exemplo abaixo do nosso sistema de ServiceDesk), o que é extremamente flexível porém não suporta intellisense nem é compilada (o que pode facilitar erros no caso de refactors). Mas, para o HQL não temos muita opção, a alternativa que seria usar o NH Linq ainda não é viável (pelo que eu tenho visto, ainda não suporta muitas coisas).


public int TotalInbox(User user) {
   const string hql =
       @"select count(*) from Incident i inner join i.Allocation a
         inner join a.SupportGroup s inner join s.Users u 
         inner join i.Company c
         where i.ClosedOn is null and a.SupportUser is null
         and i.State != isnull(c.WaitingUserConfirmationState,-1) 
         and i.State != isnull(c.WaitingUserInformationState, -1) 
         and u = ?";
   return (int) ExecuteScalar<long>(hql, user);
}

O uso de Criterias é mais simples e é indicado para consultas com restrições diretas na entidade base (ou nas que possuem relacionamento direto com a entidade base). Porém, nos criterias é necessário especificar o nome da propriedade como string. Novamente, sem intellisense e sujeito a problemas de refactor. Como o uso de Criterias é muito mais freqüente, nós automatizamos na nossa camada de domínio a geração de uma classe estática com todas as propriedades das entidades de domínio (usando arquivos .tt de um editor T4). Desta forma, temos intellisense e no caso de refactor que cause alguma quebra, teremos um erro de compilação (já que as classes são sempre regeradas). E, conforme falado no post da camada de domínio, construímos ainda uma fluent interface para facilitar a escrita. O exemplo abaixo mostra isto em funcionamento.

public long TotalClosed(User user) {
    return Query.Where(Restrictions.Eq(PN.User, user))
           .And(Restrictions.IsNotNull(PN.ClosedOn)).Count();
}

Esta solução estava bastante adequada até aparecer a alternativa de usarmos expressões lambda ao invés de gerar as classes estáticas. Existem algumas iniciativas já fazendo isto, como o projeto NHLambdaExtensions. Neste caso específico, porém, eles estão abordando isto como uma biblioteca à parte para a geração de Criterias do NH e não como algo integrado aos repositórios.

Felizmente, na nossa camada de domíno, os repositórios já são tipados para a entidade de domínio que eles representam. Desta foram, fica simples gerar as expressões. O exemplo abaixo mostra a mesma rotina acima, agora usando expressões lamdas.

public long TotalClosed(User user) {
    return 
      Query.Where(i => i.User == user && i.ClosedOn != null).Count();
}

Como pode ser visto, bem mais legível e intuitivo. E sem a necessidade de se usar classes estáticas de apoio! Ainda temos algum trabalho a ser feito, pois não é simples traduzir todos os tipos de lambda para restrições válidas. Mas para os casos mais simples como o acima, está já 100% funcionando.

Outro ponto interessante é que as funções que aceitam lambda podem ser expostos para camadas superiores de domínio, já que usar uma expressão lambda não causa dependência para a camada de persistência. Isto diminui de maneira significativa o próprio tamanho da implementação dos repositórios.

Finalmente, a utilização deste tipo de solução acaba sendo algo tão flexível que estamos passando a adotar em vários outros cenários. Na definição de classes de ordenação para consultas, na identificação de campos usados como atributos em formulários e controllers e assim por diante. Realmente algo muito simples que está aí desde o lançamento da versão 3.5 do .Net mas que demorou para  a “ficha cair” achar uma maneira legal de aplicar na nossa infraestrutura.

Quem quiser saber algum detalhe mais técnico da implementação basta me mandar um email. Até a próxima.

, , , , ,

Deixe um comentário