Dev env as code: Vagrant up

Quem trabalha com muitas tecnologias em diferentes projetos, ou mantém diversos projetos para plataformas diferentes com certeza já passou por isso: Como manter diversos ambientes de desenvolvimento na mesma maquina? Ou mesmo como reconstruir “aquele” projeto mais antigo? Para isso e para o clássico do garoto novo na empresa ter que configurar um ambiente novo ou quando você mesmo troca de maquina, temos o Vagrant!

Vagrant é uma camada de abstração para o ambiente de desenvolvimento. Ele permite definir o ambiente de dev do projeto através de um arquivo de configuração, o Vagrantfile, que passa a ser parte do código da aplicação, logo entrando para o controle de versão, distribuição e tudo mais. A partir desse arquivo o vagrant é capaz de criar uma maquina virtual com todo o ambiente pronto.

Algumas vantagens de usar essa abordagem:

  • Ambiente faz parte do código do projeto
  • Ambiente fica documentado
  • Portabilidade (mesma VM no linux, windows e mac)
  • Maior proximidade do ambiente de produção/homologação
  • Maior isolamento entre os ambientes de diversos projetos
  • Testar projetos distribuídos

Meus dois principais use-cases foram para permitir a quem entrar novo no time ter o ambiente pronto rapidamente, e de manter os ambientes isolados (tenho apps em php, clojure, ruby 1.9, ruby 2.1, etc…) sem afetar uma a outra.

Para usar o vagrant é simples:

Primeiro instale um virtualizador (como o virtualbox) e o vagrant no seu sistema.

Em seguida, no projeto, rode o comando “vagrant init”, ele vai criar um arquivo Vagrantfile de exemplo com comentários sobre como configurar sua maquina virtual. Tendo um Vagrantfile no projeto basta executar o “Vagrant up” para subir e configurar a maquina virtual. Com a maquina no ar você pode então executar “vagrant ssh” para ter acesso a ela e ir a pasta /vagrant, a qual é compartilhada com o projeto em si.

Resumindo:

$ vagrant init # cria um vagrantfile
$ vagrant up # inicia uma maquina virtual
$ vagrant ssh # acessa a maquina virtual
$ vagrant halt # desliga a maquina virtual

Algumas configurações importantes:

Deve-se definir a máquina base usando, por exemplo:

config.vm.box = "ubuntu/trusty32"

Além disso você pode buscar no vagrant cloud por várias máquinas já prontas, pode ser que a você precisa já esteja lá.

Usando a diretiva de rede no Vagrantfile a maquina virtual fica disponível nesse IP, assim como ganha uma rede interna para todas as vms poderem se acessar:

config.vm.network "private_network", ip: "192.168.10.10"

O mais importante do vagrant é a capacidade de provisionamento do ambiente, ou seja, instalar e configurar as dependências da sua aplicação de forma simples e automatizada, usando as ferramentas mais comuns de devops:

Pode até combinar eles, exemplo:

config.vm.provision "puppet" do |puppet|
 puppet.manifests_path = "manifests"
 puppet.manifest_file = "default.pp"
end

config.vm.provision "shell", :inline "apt-get install apache2 php5"
config.vm.provision "shell", :path "config.sh"

Temos também alguns plugins interessantes.

Concluindo, com um Vagrantfile no projeto você garante que o ambiente desse projeto está a um “vagrant up” de distância. Embora o tempo para instalar e configurar a maquina inicialmente demore um pouco (afinal está configurando uma maquina totalmente nova), o isolamento e a certeza de ter um ambiente uniforme valem a pena.

Em um próximo texto faço também alguns exemplos de configurações para os ambientes mais comuns.

Novos projetos: FastChat e Semaphore.js

Sempre curti micro-projetos úteis e reutilizáveis, principalmente widgets.

Bom, para o projeto que estou trabalhando vi a necessidade de dois widgets que imaginei que seria legal de reutilizar, e eis o resultado deles (ainda em desenvolvimento):

Semaphore.js

Um mini widget em javascript que apenas indica o status do servidor através de um semáforo.

Através de um url configurada do widget ele indica se o servidor está online, lento ou offline. É bem útil para aplicações totalmente em ajax, principalmente quando se depende de servidores instáveis ou serviços de terceiros. Permite ainda algumas integrações para cada estado.

Consiste apenas em um pequeno arquivo javascript.

FastChat

Fastchat: chat widget easy to use

Esse é mais legal! É um widget de bate-papo que basta carregar um pequeno javascript sem dependências e está pronto o chat!

Foi feito com um servidor em clojure, usando redis e javascript simples para o widget. Pode-se usar css para customizar a UI padrão, grava um histórico, possui uma “janela” por usuário, lista de onlines e etc.

Bom, é isso, o semaphore.js eu já usei em mais de um projeto e o fastchat ainda está em teste no projeto principal, mas acho que promete bastante.

O padrão Repository para persistência de dados.

Há muito tempo atrás, em uma terra distante, escrevi um artigo explicando a diferença e a abordagem nos padrões mais comuns de persistência de dados: Active Record e DAO. Que na verdade era parte de uma série sobre boas práticas em programação.

Lá no fim, disse que escreveria sobre outro padrão, o Repository. Muito tempo se passou, e eu me esqueci disso. Mas o Frederico me lembrou (obrigado), então aqui estou.

O padrão Repository (ou Repositório) tem, em uma abordagem muito semelhante ao DAO, a responsabilidade de representar a camada de dados na aplicação. A semelhança entre DAO e Repository é tanta que em muitas abordagens são programados da mesma forma, ou simplesmente delegam do Repositório para o DAO. Em casos de migração onde se tenta “uma abordagem mais Domain Driven” simplesmente refatoram os DAO para se chamarem Repositório.

Porem os padrões atuam em camadas distintas da aplicação, e podem até mesmo coexistir.

 

Padrão de projeto Repository explicado

 

 

Os Repositórios pertencem ao modelo da aplicação, são parte de camada de negócios complementando o Model e fornecendo estes objetos as outras camadas (como Controle e Apresentação, se aplicável) e recuperando estes do banco de dados.

Porem, sendo parte do modelo, os repositórios não conhecem detalhes de infra-estrutura da aplicação (banco de dados, http, etc), e é na área de infra em que atuam os DAOs. Assim o DAO tem o trabalho de traduzir as chamadas à persistência em chamadas de infra, sejam elas banco de dados, webservices, arquivos em disco ou outra abordagem qualquer.

A confusão acontece por que na maioria dos projetos toda persistência e acesso a dados é feita através de um , e apenas um, banco de dados. E , mais ainda, os frameworks atuais abstraem e traduzem os objetos em relacionamento de forma tão simples que o DAO simplesmente passa ao framework o que recebe e retorna o resultado.

Dessa forma , em sistema onde temos um modelo Usuário, teríamos um UsuárioRepository atuando no modelo. Este repositório faria a interface de criar, recurar e guardar os Usuários, e pode incluir métodos específicos, como filtros ou pré-preencher os usuários.

Mas o repositório não sabe de infra, então ele poderia ter de delegar a um DAO as chamadas. Desta forma os DAO apenas implementam as chamadas mais genéricas como select, insert, update e delete. Em um framework atual, que oferece essa interface de forma simples, o repositório poderia passar a chamada ao framework, se a configuração não passar pelo repositório.

Então, ao receber uma chamada o Repositório valida isto de acordo com as regras de negócio que o cabe(já que a maior parte pode estar no próprio modelo), e pede à camada de infra esses dados. A camada de infra trata essa chamada no cabe à infra ( valida, encode, traduz campos…) e acessa a persistência. Ela trata o retorna da persistência no que vale a infra (array para objetos, encoding, joins…) e devolve ao repositório. O repositório então trata este retorno o que diz respeito ao negócio, criando objetos, cálculos e o que for aplicável.

Como disse, em muitos projetos isso gera classes desnecessárias e o mal uso dos repositórios. Uma forma diferente de lidar com isso é que os Repositórios são interfaces, implementadas pelos próprios DAO(o contrário não é valido), pois do ponto de vista de modelagem o modelo continua acessando o Repositório. E pode-se criar um repositório implementado aonde preciso, antes do DAO. Esta forma parece um pouco mais complexo, mas é ate mais simples.

O que temos afinal é: O Repositório é uma camada de negócio da aplicação, responsável por manter e persistir os objetos de Modelo, enquanto isto não envolver infra. Quando tratar de infra, esse trabalho é delegado aos DAO ou diretamente ao framework, caso este seja bem abstraído.

IdeasWall no project Kenai (e no GitHub)

O projeto Kenai é a iniciativa da Sun para prover aos desenvolvedores ferramentas aonde eles podem hospedar seu código fonte e se conectar, comunicar e colaborar com outros desenvolvedores. Basta se cadastrar, e é gratuito.

Você pode colocar seus projetos lá e ganha um repositório(que pode ser SVN, GIT ou Mercurial), ou apontar para um repo externo,  um forum, wiki, IM, lista de email, “bug tracker”, downloads e , é claro, se integra com o netbeans(ainda beta). A principal limitação é de 5 projetos por perfil.

Anyway, agora a o IdeasWall.org está no kenai, com um forum e wiki ainda desatualizados. De bônus, o código fonte da interface em JavaFx esta disponivel no Github, para quem quiser fuçar, já atualizado para JavaFx 1.2. 

Descontrair: A arte da programação

Um conto zen:

Eis que em uma aula de formação de monges programadores Zen, o aprendiz mais jovem perguntou:

"Como devo fazer:
static double fac(int x) {double fac = 1 ;while(x>0) fac=fac*x– ; return fac ;}
ou
static double fac(int x) {if(x==1) return 1; else return x*fac(x-1) ;} ? "

Vários, prontamente e cheios de pompa, responderam:
def fatorial(number)
  if (number == 0)
    return 1
  else
    return number * fatorial(number – 1)
  end
end

Alguem disse, em voz baixa, como se não fizesse questão de falar:
def fatorial(num):
  if num <= 1:
    return 1
  else:
    return num * fatorial(num – 1)

Um dos alunos mais antigos diz desdenhando:
int fatorial(int num) {
  long fat;
  for(fat=1;num > 1;num–) {
    fat= fat*num;
  }
  return fat;
}

Eis que o mestre enigmaticamente responde a todos, que ficam perplexos:
(defn fac [x] ( if (= x 1) x (* x (fac (- x 1)))))

Após os ensinamentos, todos voltam a sua rotina e estágios, onde se deparam com a dura realidade da resposta:

function fac($num) {
 for($fac=1;$num>1;$num–){
  $fac = $fac * $num;
 }
 return $fac;
}

As vezes a resposta é que você menos deseja aceitar.

Ao voltarem no dia seguinte, cheios de questões sobre a vida e Scrum, encontram o mais Jovem caído em frente a porta, em estado de choque, babando, com uma singela mensagem em suas mãos:

Function fatorial2(valor)  
 Dim i, mult
 mult = valor  
 For i = valor-1 To 1 Step -1  
  mult = mult * i  
 Next  
 fatorial2 = mult  
End Function 

Neste dia, ninguém alcançou a iluminação.

Então o acadêmico mestre, entristecido por seus alunos que não podiam ver a luz fora da sala, perguntou a grande oraculo sobre a questão de seu perdido aprendiz, este definiu, sem mais:

fatorial

E o mestre finalmente entendeu o que deveria ensinar. 

Movendo meu repositórios para o git

Esotu migrando esta semana alguns projetos para o GITHub, para centralizar esses arquivos e para facilitar quem quiser acompanhar.

No geral são pacotes utilitários nos sistemas que produzo, e atualizo conforme a necessidade. Além disso mantenho ainda a página no PHPClasses.org para alguns pacotes quando já estão "supinpa".

Para quem se interessar: http://github.com/diogok. E o do phpclasses.

Have fun! 

Sistemas web RESTful

Um modelo de arquitetura para sistemas web que me chamou muita atenção um ano atrás(tanto que fiz dois trabalhos para faculdade sobre, que infelizmente perdi) foi o REST, elaborado por, nada mais, nada menos, que Roy Thomas Fielding, que participa de muita coisa importante para a internet, essa arquitetura descreve simplesmente a web como ela é.

Consiste em utilizar de os recursos base da web(o HTTP) para construção de webservices, sem overhead e envelopes extras, tornando a interação(e o desenvolvimento) simples e não burocrática, explora os recursos já prontos, suficientes e comprovada eficiencia(afinal toda a web usa) do HTTP. Usa os conceitos que tornam a web o que ela é hoje.

Um serviço REST(ou RESTful) é definido por recursos, que possuem uma representação e um estado, estado esse que pode ser mudado(ou transferido) através de ligações(os links). Não é tão complexo como pode parecer:

Recursos é o item a ser tratado, são por exemplo usuários, produtos ou artigos. A Representação desse recurso é a pagina a ser apresentada, como o HTML ou o XML ou o JSON, essa representação contém o estado do recurso. O Estado são os dados do recurso(nome, preço, texto…), o cliente muda o estado na aplicação através de links, requisitando estados diferentes e trocando o recurso. Links são muito importante, tudo deve estar "hiperlinkado".

Comparado com Orientação a Objetos, o Recurso seria o Objeto, o Estado é o próprio estado do objeto(os atributos) mas os métodos nós vemos logo adiante. A representação é a apresentação desse objeto em um formato de mídia. 

É assim que servimos páginas na web, uma página é um recurso demonstrando o estado da aplicação, e nós mudamos esse estado seguindo os links que nos são apresentados.

Mas e a implementação? Rest se baseia em duas tecnologias principalmente, são itens já muito bem testados e que usamos todo dia: URL (ou URI) e o HTTP.

Cada recurso possui um identificador, que é o seu endereço em um sistema. Cada recurso possui uma URI (Uniform Resource Identifier). Você requisita essa URI e lhe é devolvido o estado desse recurso.

O protocolo para RESTful webservices é o HTTP, dividamos ele em quatro partes:

  • O Método da requisição (também chamado de verbo) define a ação que vamos tomar.(PUT, POST, DELETE, GET, OPTIONS, HEAD…).
  • A URI da requisição define o recurso sobre o qual aplicar o método.
  • E o Tipo (MIME/TYPE) define qual representação vamos renderizar e devolver. É o cabeçalho ACEPT-MIME em preferência a "extensão da URL".
  • Com base nesses dados formatamos a Resposta e enviamos de volta ao consumidor.

Na prática funciona mais ou menos assim:

  • Uma chamada HTTP "GET /user/login" exibe(get) os dados do usuário(recurso) no formato padrão, já que não especificado.
  • O HTTP "GET /user/login/posts.json" retornaria os "posts"(subrecurso de usuário) no formato JSON.
  • Já "POST /article" está inserindo um artigo, enquanto "POST /article/12" pode atualizar o artigo 12.(Poderia, ou até deveria, ser usado o PUT para atualizar, mas há controvérsias).
  • A requisição "POST /article/12/comment" pode adicionar um comentário ao artigo 12.
  • Seguindo assim, a ação de "DELETE /article/12/comment/2" exclui este comentário e "GET /article.xml" retorna a lista de artigos em XML. 
  • Pode até usar "GET /article/12.pdf" ou "PUT /user/diogo/picture.jpg".

O que é mais importante aqui é a conformidade do uso do HTTP. Um requisição do tipo GET deve apenas retornar dados, nunca alterar nada. Uma do tipo POST deve atualizar ou inserir um recurso, idem para o PUT. Recuperar dados nunca deve depender de POST, e um GET nunca deve alterar nada. Isso é importante para manter a consistência da aplicação, e podermos confiar nos métodos. Dois acessos seguidos de GET(ou HEAD ou OPTIONS) em um recurso deve ter o mesmo efeito que três ou nenhum, enquanto POST ou PUT podem fazer diferença.

REST trás, pelo HTTP, bons recursos prontos para serem usados:

  • Cache eficiente, através dos Headers devidos do HTTP (expires…).
  • Autênticação, BASIC ou DIGEST
  • Segurança através de SSL (HTTPS)
  • Falta de estado(confusão aqui, leia a seguir), por falta de necessidade.

REST é stateless, isso que dizer que ele não mantém o estado do cliente. Bom, nesse caso não é o mesmo estado que falei mais acima. O Estado do Recurso são seus atributos, e são mantidos no servidor(Banco de dados, Filesystem…) e o estado do cliente é o ponto em que a aplicação que consume o serviço esta(ou a seção).

Dessa forma o fluxo ou ordem da navegação do cliente não afeta qual recurso vai ser apresentado, e nem o cliente precisa manter informação sobre em que ponto parou para poder fazer uma requisição.

Um bom link é o REST: Paul James, muito conteúdo bom lá. 

Como podem ver REST não é nada novo, já fazemos a web assim a muito tempo, mas agora temos uma forma mais elegante e um bom motivo para manter a simplicidade que faz da web o que ela é.  

Camada de persistência de dados: DAO e ActiveRecord

Finalmente a camada de dados…

A camada, ou layer, de persistência ou de acesso aos dados é a parte da aplicação responsável por se comunicar com o banco de dados, ou com o framework de persistência, sendo os dois padrões mais conhecidos o DAO e o Active Record.

 

Active Record Vs DAO: O Show!

 

 

Numa aplicação orientada a objetos, e bem modelada e modular, temos a boa separação entre as responsabilidades de cada parte da aplicação. A parte de acesso ao banco de dados é uma das mais interessantes (a minha camada favorita!), ela é responsável por se conectar ao banco de dados e extrair, inserir e atualizar as informações. É responsável também por transformar modelos de Objetos em modelos Relacionais, o tal do ORM(Mapeamento Objeto Relacional), já que em muitos casos lidamos com banco de dados relacionais.

Podemos acessar de duas formas o banco de dados, usando um framework ou escrevendo SQL próprio. Hoje em dia existe uma variedade de frameworks, que programam os vários recursos necessários, de modo que costuma ser perda de tempo fazer o acesso direto ao Banco de Dados. Geralmente o ORM é feito através desses frameworks.

Temos dois padrões comuns para a comunicação com o BD/Framework: o Data Access Objet(DAO) e o Active Record (AR). Poderíamos acrescentar também o padrão Repositório, mas este terá um artigo próprio, na evolução deste tema. Os dois, DAO e AR, são padrões bem distintos e refletem abordagens extremamente diferentes na modelagem da aplicação.

 O Active Record se tornou extremamente popular com o framework Rails, para a linguagem Ruby, como a produtividade e simplicidade do framework revoluciona o desenvolvimento muitos abraçaram o padrão também em outros frameworks. O padrão é muito comum em frameworks para linguagens dinâmica, sendo até difícil achar um que não o use(nem tanto…).

 

Active Record Design Pattern

 

A modelagem em Active Record "invade" a camada do Modelo/Domínio da aplicação, definindo que um Objeto do modelo é o reflexo de uma "linha" do banco de dados. Assim sendo, os objetos que são persistentes devem estender/especializar uma classe ou interface que seja equivalente a uma linha do banco de dados. O Modelo é uma Extensão do Banco de Dados.

Essa interface, que o modelo ou classe genérica programa, define os métodos de interação com o banco de dados que um modelo possui. Então o próprio modelo é responsável pela sua persistência. O próprio objeto implementa métodos como Salvar, Restaura, Filtrar e etc. Além das funções de "Join". Desta forma o Active Record tem uma abordagem simples, visto de outras camadas é uma forma lógica e fácil de se trabalhar..

Nos frameworks, temos uma classe abstrata que é capaz de decidir como montar as queries SQL de acordo com as propriedades do objeto que a estender. Desse modo costuma bastar estender e não há necessidade de alterar o modelo, no máximo acrescentando informações ou meta-dados sobre a tabela equivalente, de acordo com a abordagem do framework.

O problema com o Active Record é que seu modelo fica com muita responsabilidade, Activer Redord não Escala!, e cria "BFC"s (Big Fucking Classes), já que o modelo além de conter as regras de negócio também cuida do banco de dados. Essa abordagem do Active Record é muitas vezes considerada uma falha no design da aplicação, pelo fato de que o Domínio passa a ser subordinado do Banco de dados. Este argumento pode ser rebatido já que na maioria dos sistemas, as classes do Modelo são apenas representações do Domínio, e não programam regras em si, e que essas regras passam estar representadas na persistência, não afetando o design de muitas aplicações.

Na prática, se temos uma classe Usuário esta deve estender uma outra classe, digamos Record. Record é uma classe provida pelo framework de persistência, ela implementa os métodos Select, Save e Delete. Ela também exige que seja implementado um método Configure pela classe filha, para definir os parâmetros do banco de dados. Assim sempre que precisamos buscar um usuário do banco de dados, usamos o método Select da própria classe Usuário, e por ai vai.

Se por um lado o Active Record atua de forma simples e explorando a flexibilidade dos frameworks de persistência de hoje e dinamismo das linguagens, o DAO por sua vez surge num ambiente de framework mais rígidos e que demandavam maior numero de chamadas e configurações. O DAO é praticamente o oposto do Active Record, em design. 

As classes DAO representam uma camada própria, e formam um pacote de acesso de dados, algumas vezes sob o pacote do modelo, algumas vezes um pacote independente e outras vezes parte do pacote de controladores. O mais comum é mesmo que o pacote de DAO fique subordinado ao Modelo, mas sem estendê-lo. Assim temos uma separação e relativa independência da camada de acesso dados e do Domínio.

 

DAO: Data Access Layer

 

 

O principio é que para cada Modelo, temos um DAO correspondente. Toda interação e configuração com o Banco de dados, ou com o framework de persistência, ficam na camada dos DAO. Então o DAO passa a programar métodos como Select, Delete, Insert, Update e outros que venham a ser necessário. Podem programar métodos mais específicos, como selecionarOndeNomeComecaComLetra ou cadastrarLista.

O DAO pode ser usado também como camada entre a aplicação e o modelo, ou entre diferentes aplicações. Passa-se a não acessar o modelo diretamente (com new) e usar os métodos do DAO para tal, mas essa abordagem nem sempre é usada. Assim, sempre que quiser uma instância de um Modelo, usa-se o DAO.

O uso de DAOs é(ou era ao menos) bem mais comum nos projetos Java. Como muitos frameworks ainda eram muito rígidos, e precisavam de mais código para fazer o ORM, ou para escrever queries mais personalizadas, esse código passou a residir na camada do DAO, algumas vezes tanto a descrição da transformação ORM quanto a montagem das queries através dos métodos do framework. Ou então, em casos onde as queries deviam ser muito precisas, a interação direta com o Banco de dados.

A abordagem de DAO costuma(va) ser associada a um design mais elegante, devido à separação das camadas. Assim tínhamos mais flexibilidade ao manipular o framework. Porém com a evolução dos frameworks, estes passam a perder um pouco a utilidade, já que são necessárias poucas chamadas aos framework e estes conseguem criar queries cada vez mais complexas automaticamente.

O DAO entra em "crise" com a evolução dos Framework, e sua utilidade passou a ser questionada, pois muitas vezes passaram simplesmente a programar os mesmos métodos que o framework provia, apenas repassando os chamados. Ficando entre um caso de programá-los por puro design e passando a ser overengineering. Com o conhecimento do padrão de Repositórios esses caso passou a ser entendido de outra forma..

Na prática, se temos um modelo Usuário, teremos um DAO UsuárioDAO. A classe Usuário não se altera, mas implementamos na UsuárioDAO os métodos necessários, como select, save e delete. Os métodos de UsuárioDAO fazem as validações necessárias e montam as chamadas para o framework de acordo com as especificações do Usuário, repassando então o objeto ao framework. Pode ainda implementar métodos especificos como validaLogin. Então ao precisar criar, recuperar ou salvar um objeto usa-se o DAO deste.

Apesar de ter escrito bastante, esta abordagem ainda é superficial. Apresentei os principais conceitos destes padrões, para exemplos práticos basta procurar frameworks para sua linguagem. Para o Active Record existem várias opções, mas na verdade não existem framework para DAO, já que DAO pode ser usado em conjunto com outras abordagem de persistência.

Um outro importante ponto na camada de persistência e acesso aos dados, é o padrão Repositório (repository), que solucionou alguns problemas dos DAO e se adapta melhor a estes frameworks mais flexíveis e menos complexos, mas este fica para um próximo texto.

Boas práticas em Orientação a Objetos

Desenvolvedores, programadores e adeptos, ao desenvolver aplicações e sistemas, há algumas guias que nos ajudam a criar um bom código, fácil de manter, de entender e que outros programadores vão poder mexer caso necessário. O primeiro passo para isso é o uso de Orientação a Objetos. Mas mesmo dentro deste, há alguns conceitos importantes de serem colocados. Eis alguns pontos, ainda rascunhados, que é bom ter em mente:  

Uma bela interface em PHP 

  • Tempo
    • Fácil manutenção
    • Fácil de entender: O código é semântico, sem confusões e se auto-explica. Comentário apenas onde é necessário. O Código se auto-documenta.
    • Fácil de testar: Unidades simples, reuso, testes automatizados. É mais fácil testar pequenas unidades.
  • Modularidade
    • Acoplamento: Baixo acoplamento, permite melhores testes mais independência do código e maior reuso.
    • Dependência: Use apenas onde necessário.
    • Isolar mudanças: Segurança com testes. Mudanças de framework, de infraestrutura.
  • Não se exponha muito
    • Independência de implementação: Mudanças na implementação, no código de um método, não afeta o comportamento de onde usa este método.
    • Privado / Público: Propriedades privadas, assessores públicos. Protege as informações.
    • getters e setters: Muda a implementação, a fonte de dados, a regra de negócio. Ficam os assessores, a interface.
  • Responsabilidade
    • Sempre retorne algo significante: Evitar retornar null. Interface encadeada. No mínimo, retorne boleano.
    • Trate entradas inválidas: Não confie no resto do código. Valide sempre. Trate Nulos. (Especial foco em linguagem de tipo dinâmico/fraco)
    • Cuide de seus erros internamente: Use exceções com significado.
  • Interfaces
    • Exponha suas interfaces: Maior reuso, maior independência. Evitar acoplamento especifico.
    • Use interfaces com significado: Código é documentação.
  • Padrões
    • Pense em padrões: Resolver problemas de forma padronizadas. Nem sempre são os "Padrões de Projetos"(design patterns).
    • Use herança corretamente: Cuidado com armadilhas por facilidade de uso. Herança tem que fazer sentido. Evite BigFuckingClasses.
    • Use composição corretamente: Composição sobre herança. Corrente de responsabilidade, interfaces e acoplamento. 

Ficou bem confuso…e ainda existe muito mais. Eu uso a seguinte métrica para saber se a modelagem esta legal: Se eu consigo explicar de forma clara o por que de ser assim, é por que esta no caminho. Se complicar para explicar/desenhar o modelo, é por que esta ruim.

Para que toda essa modelagem, camadas, TDD, padrões… atoa?

ATENÇÃO: TEXTO QUASE EMOTIVO SOBRE DESENVOLVIMENTO DE SISTEMAS. BASTANTE BLABLABLA FRENTE. CUIDADO.

Trabalhando em um projeto pessoal me ocorreu: Será que preciso mesmo dividir a responsabilidade do sistema, dividir as camadas e ainda me preocupar em baixo acoplamento e com semântica?
Vou explicar-me melhor: Meus projetos pessoais são a oportunidade de colocar em prática as várias idéias e teorias sobre desenvolvimento e arquitetura, que se bem aplicados passarão para os proximos clientes. É a chance de fazer o sistema perfeito, de modo que uma engenharia reversa para UML resultaria numa obra de Picasso. São projetos reais, onde tenho a chance de testar frameworks, metodologias e etc.

Quer dizer, são projetos que geralmente nunca termino. Por que quase sempre aparece uma idéia legal para testar. Mas estou perdendo o foco do texto… 

Esboço de arquivos do projeto 

Nesse projeto aplico Desenvolvimento guiado a testes(O TDD), divido as funcionalidades em pequenos métodos, baixo acoplado, re-uso tudo que posso… sempre refatorando, me preocupando se as classes não ficam complexas demais, se não fogem a sua responsabilidade. “Será que um Repositório pode usar outro Repositório?”, “Essa uma pesquisa em massa no banco esta no escopo dessa classe?”, “Esse nome transmite o sentido do método? E esse retorno é apropriado?” e etc… assim nos mínimos detalhes. Ai parei para me perguntar, será que preciso disso tudo?

Quer dizer, especialmente para começar no TDD (desenvolvimento guiado a testes…) é bem chato, é um começo difícil. Por que as classes as vezes não são fáceis de testar, são muitos testes, muitos casos a cobrir, tem que ter um banco de dados para isso… são várias “pedras no caminho”.

As vezes parece que seria muito mais rápido escrever uma pagina para lidar com cada requisição, acessar o BD direto ali e simplesmente fazer acontecer a mágica do sistema… não é mais fácil? Tudo esta no escopo, me guiar pela requisição e pelo retorno apenas. Não é mais fácil?

Recebi de braços abertos e sem defesa a resposta, como uma flecha varando meu peito, fria: “NEM FODENDO NÃO É MAIS FÁCIL!”. Senta que lá vem mais história.

Essa semana mesmo estava fazendo manutenção numa aplicação mais antiga que desenvolvi. É um ERP, uso crítico da empresa, na época ainda estava aprendo OO, então mal tinha esboço de classes naquilo ( Só tinha um classe para o BD, e os modelos que a estendiam… modelo estendendo BD! Triste mesmo…). E o pior que era organizada até, exista um quase MVC por trás da aplicação, mas conforme adcionava funcionalidades as pressas, tudo ia para debaixo dos panos.

As responsabilidades não eram bem dividas, não tinha um rotina de testes básica, camadas são para os fracos. Ou seja, o sistema se tornou imprevisível! A cada clique eu temia que o banco de dados fosse para o espaço e meu processador fritasse. Para garantir que uma nova regra de negócio acontece por todo o sistema, eram várias alterações, em vários lugares as vezes desconexos. “Adicionar porcentagem de aproveitamento: arquivo salvar_conta, classe conta (nos 5 loops dos 3 métodos), arquivo benchmark ( no loop 2, 5 e na exibição).”

 

Mal Modelo

 

 

Numa mesma aplicação desenvolvida pouco tempo depois, ainda não tinha os testes automáticos e o mesmo cuidado de hoje, mas já tinham camadas, modelagem, interfaces… é moleza. “Quer mudar uma posição da foto, vai na View”, “As imagens não estão armazenando corretamente, será no uploader ou no tratamento de imagens?”,”Uhm, não esta recuperando esse campo do BD, abre o DAO.”, é padrão, tem poucas dúvidas e a implementação é rápida, acha-se os problemas facilmente.

O conhecimento ganhado é gradativo: OO, camadas, Repositórios, frameworks, Testes… quanto mais é aplicado, mais fácil é dar manutenção. MANUTENÇÃO é a palavra. Só parece que é mais fácil fazer sem preocupação, mas isso só vai ficar legal por uma ou duas páginas. Quando o sistema começa a se conectar, quando começam a surgir alterações(e sempre, SEMPRE surgem!) ai que faz falta a boa modelagem.

Obviamente também tem que tomar cuidado com o “overengeneering”, modelar demais pode ser perigoso e criar correntes de responsabilidades sem sentido entre outros. Tem que estar sempre em busca do ponto de equilibrio.

Especialmente de um tempo para cá, quando comecei a aplicar o TDD, depois de muito lutar contra(tenho que admitir), comecei a ver os benefícios. É muito mais que garantir que os métodos estão retornando certo… é a chance de garantir a boa implementação do sistema. Os testes são as métricas do bom código.

Se esta difícil escrever uma rotina de testes consistente, é um sintoma que a modelagem não esta muito boa: Os métodos estão com muita responsabilidade, ou tudo esta muito acoplado. Se a responsabilidade esta bem divida, há bastante re-uso e os métodos fazem sentido, é fácil bolar os testes.

Os testes são também uma boa documentação. Quer saber como um classe deve se comportar, como ela será usada? Veja como foi testada, quais os pontos críticos do teste.

Quer saber qual implementação é mais rápida, ou tem melhor performance? Coloque marcadores nos testes, contadores. Adicione uma ferramenta de profiling. Você vai ter a garantia que mudou a implementação, o sistema esta funcionando igual, e saber se ficou mais rápido ou não.

Bom modelo 

Uma análise boa por exemplo é: E se eu mudar de framework(ou adotar um) para área XPTO, o quanto isso vai afetar o sistema? Bom, a mudança deve ser transparente, a dependência do framework e infraestrutura em geral deve ser bem separada. E os testes devem garantir que tudo continua funcionando da mesma forma após a mudança.

É, foi um desabafo sim, mas foi sincero. Seguimos aprendendo ;D