Concrete Solutions estará em Belém para recrutamento de desenvolvedores Android e iOS

Alô, desenvolvedores de Belém!

Dia 08/08, Olavo Castro (Arquiteto de Soluções) da Concrete Solutions estará em Belém para bater um #papoSafo com a Comunidade. Na ocasião, ele irá apresentar a palestra:

 

Times mobile: uma trajetória de aprendizado

Descrição:

Esta palestra tem por objetivo mostrar como a Concrete Solutions conseguiu montar e manter times de alta performance em um cenário de constante mudança como a mobilidade, considerando um aprendizado de 12 anos e o jeito certo de fazer software atualmente: com métodos ágeis, Lean e efetivo gerenciamento de produto.

Ao final, será lançada uma seletiva para recrutamento de desenvolvedores Android e iOS para trabalharem em São Paulo. Mais detalhes em http://www.concretesolutions.com.br/carreira/.

E aí, ficou interessado? Então não perca essa oportunidade e garanta sua vaga fazendo sua inscrição neste link.

Anúncios

Código legado. O Horror!!

Cthulhu

Cthulhu

Existem, na comunidade, várias definições para o que seria código legado: todo código que você escreveu é legado; o código que outra pessoa escreveu é legado; o código ruim, de difícil manutenção é legado. Não importando a definição, podemos identificar uma característica primordial, o código legado é aquele difícil de mudar. E isso, geralmente é um terror para os desenvolvedores.

Quem escreveu o código, não necessariamente é um desenvolvedor ruim. Precisamos entender o contexto. Mas gosto da idéia de que a qualidade do código é um espelho do local onde ele foi desenvolvido. Se o código é ruim, a empresa pode apresentar sintomas graves de uma péssima gestão, principalmente em se tratando de comunicação inter-departamentos, já que normalmente as demandas não vem de TI. Nesse cenário, será bem difícil trabalhar em melhorias do código legado, enquanto não houver melhorias nos processos internos da empresa e um alinhamento de negócios com TI. Mas, ainda assim, agente tenta.

Já tive a oportunidade de trabalhar em muitos projetos legados. Sei que você também tem muita história pra contar a respeito de suas aventuras nas matas densas do legado. Você irá achar que, a partir daqui, eu estarei exagerando e querendo causar polêmica, mas não, o que vou contar é a mais pura verdade. Já trabalhei com código que dava vontade de vomitar só de olhar pra ele. hahaha. Não é pra tanto, mas já chegou perto disso.

Trabalhei em projetos legados bem complexos e com uma base de código bem ‘doente’, normalmente monstros monolíticos. Mas apesar disso, os softwares eram, e são até hoje, bastante elogiados. Assim como para seres humanos o que importa é o caráter e não a aparência, para software o que importa é o valor que ele entrega ao cliente. Mas isso é só um lado da moeda. Aqueles sistemas tão elogiados de que falei faziam bem o seu trabalho, mas apresentavam uma base de código extremamente difícil de gerenciar, o que dificultava a criação de novas funcionalidades e melhorias.

Outro fator que considero importante em um cenário como esse é a satisfação com o seu trabalho. É bem ruim ter que trabalhar todo dia fazendo remendos em uma estrutura ‘capenga’. É desmotivante, é cansativo e isso pode aumentar a rotatividade em sua empresa. E a alta rotatividade de desenvolvedores é um fator que contribui a gerar mais código ruim. É extremamente frustrante quando, devido a algum fator, ou vários fatores, não é possível melhorar a base de código.

Você pode achar que isso tudo é muito mimimi. Mas que fique claro que o maior prejudicado nessa história é a empresa que mantém o código. Se não for possível fazer melhorias, a empresa terá que conviver com os custos de se manter um legado ruim. A escolha é sua, ficar no mimimi ou partir pro ataque.

Enfrentando o monstro

Uma outra definição para sistemas legados, e essa foi a que mais gostei, nos diz que sistema legado é todo sistema que não está coberto por uma suíte de testes, ou seja, você estará construindo código legado toda vez que você escreve código sem testes. Interessante. Então fica a dica: sempre que for refatorar, escreva um teste, se bem que refatorar sem testar não faz sentido. Mas quando se tem uma base de código muito grande e complexa, isso não é tão fácil. Além disso, você irá encontrar no legado muitos recursos que nem são utilizados. Puro lixo.

O código legado tem a característica de ser bastante acoplado. Então uma forma de começar a refatoração é isolando as dependências e trabalhar em cima delas. Inicialmente pode ser inviável criar testes de unidade, mas é possível criar testes de aceitação e isso pode ser um bom começo.

Outra forma seria o que Martin Fowler chama de estrangular a aplicação. Você vai criando um novo sistema nas bordas do antigo, muitas vezes de forma transparente ao usuário. Não é uma tarefa tão simples, principalmente se precisar rever a modelagem do banco de dados. É um processo bastante demorado e pode durar anos, mas é eficaz. É importante estar alinhado com a gerência e que ela saiba o que você está fazendo, mas algumas vezes é mais fácil pedir perdão do que permissão, essa é a verdade.

Um outro cenário é reescrever tudo. Isso pode ser possível, mas tem que ser analisado com cautela. Dependendo do contexto pode ser a melhor solução, ou pode ser catastrófico. Tive a experiência de trabalhar num sistema legado que estava até bem escrito. O problema é que ele havia sido desenvolvido com um framework caseiro que possuia geradores de código e o dono não estava mais na empresa. Não havia muito o que fazer. Resolvemos reescrever tudo com um novo framework que estávamos testando na época (um framework bastante conhecido e usado pela comunidade). Um fator que ajudou bastante é que a modelagem de dados do sistema legado estava impecável, então reaproveitamos o banco.

Uma dica final é sempre seguir padrões. Estar atento ao que a comunidade recomenda e participar de eventos é importante. Você pode estar sendo o vilão escrevendo o legado de amanhã. Poupe sua mãe de xingamentos. Lembre-se que o código não é seu, mesmo que você o tenha escrito.

referências:

Michael Feathers, Working Effectively with Legacy Code, Prentice Hall, 2005.

Martin Fowler, “StranglerApplication”, http://www.martinfowler.com/bliki/StranglerApplication.html.

Mary e Tom Poppendieck, Implementando o Desenvolvimento Lean de Software

Criando abstrações com repositórios

Ainda hoje vejo muita gente utilizando o padrão DAO em novos projetos, mesmo usando novas tecnologias de persistência como Hibernate e JPA. Não vou entrar em discussões sobre o padrão DAO estar morto ou não. O fato é que em projetos legados, principalmente os que usam JDBC, o DAO está bem vivo. Mas e em projetos que usam ORM? É necessário usar DAOs e os famosos DAOs Genéricos?

Usar DAOs, hoje, pode ser algo extremamente redundante. Pois as ferramentas de ORM já implementam o padrão DAO. Por que você implementaria novamente? Mas, claro, mesmo em projetos novos, pode ser que apareça o cenário para se utilizar o DAO. É uma decisão de design que não deve se basear em senso comum. Projetar software não é uma tarefa trivial, porém, muitos ainda se baseiam em receitas de bolo e não param para trabalhar no design da aplicação. Mas nós aprendemos a criar DAOs na camada de persistência. Como fazer de outra maneira?

Lembre-se que o DAO existe para abstrair a camada de persistência e criar uma interface para o acesso aos dados. Hoje isso já está pronto e quem o faz é a ferramenta de ORM. Em determinados cenários não vejo problema em injetar o EntityManager do JPA direto no Controller. Algumas vezes não existe motivo para se criar uma camada desnecessária. No entanto, é mais usual que tenhamos que fazer consultas usando HQL ou JPA QL, ou que seja necessário o controle manual de  transações. Esse tipo de coisa jamais deve ficar na camada de aplicação. Existe uma forma mais elegante de abstrair a camada de persistência. Criando repositórios.

Repository é um conceito do Domain Drive Design. A camada de domínio de uma aplicação é onde se encontram as regras de negócio, logo, o que ocorre nessa camada está na ‘boca do povo’, ou seja, está nos brainstorms, reuniões de levantamento de requisitos, e nas conversas entre desenvolvedores e pessoal de negócios. A camada de domínio precisa ser escrita no que chamamos de linguagem ubíquoa, a linguagem comum entre devs e negócios.

O que normalmente encontramos dentro de controllers é algo assim:


BookDao dao = new BookDAO();
Book book = dao.findById(id);

Primeiro, se estivermos usando ORM, o DAO é desnecessário. Segundo, é feio de ler. Não tem nada a ver com uma liguagem de domínio. É muito comum as pessoas somente trocarem o nome DAO por Repository e modificar a interface método.

/**

*/
public class BookRepository {

  public final EntityManager entityManager;

  public BookRepository(EntityManager entityManager){
    this.entityManager = entityManager;
  }

  public void add(Book book){
    this.entityManager.merge(book);
  }

  public Book findBook(String id){
    return this.entityManager.find(Book.class, id);
  }

}

A chamada fica assim:

  BookRepository bookRepository = new BookRepository();
  Book book = bookRepository.findBook(id);

Bem melhor, mas ainda temos alguns problemas. Muitos desenvolvedores costumam usar o termo repository no nome das classes, assim como fazem com o dao. Devemos usar nomes significativos para nossas classes, métodos e variáveis, principalmente em se tratando de classes de domínio. Que tal se mudarmos o nome da nossa classe para Library?

  Library library = new Library();
  Book book = library.findBook(id);

Uma coleção de livros, geralmente é uma biblioteca, não é verdade? Mas ainda há um problema. O alto acoplamento com a implementação do repositório. Em se tratando de uma classe de repositório, ela poderá ser usada em vários locais, tanto em controllers, quanto em classes de serviços e até mesmo por classes de modelo. Mesmo usando injeção de dependência, ainda ficamos acoplados com a camada de persistência. Então podemos criar uma interface.


public interface Library{

  void add(Book book);

  List<Book> allBooks();

  Book findBook(String id);

}

E fazemos com que a classe repository implemente a interface Library.


public class BookRepository implements Library {

  public final EntityManager entityManager;

  public BookRepository(EntityManager entityManager){
    this.entityManager = entityManager;
  }

  public void add(Book book){
    this.entityManager.merge(book);
  }

  public List<Book> allBooks(){
    return this.entityManager.createQuery("select b from Book b", Book.class).getResultList();
  }

  public Book findBook(String id){
    return this.entityManager.find(Book.class, id);
  }

}

Agora através de injeção de dependência, as classes podem receber a interface Library como dependência, e não mais depender de sua implementação. Podemos criar uma camada de persistência e inserir as classes *Repository nela enquanto que as interfaces ficam na camada de domínio. Em Domain Drive Design, as classes que implementam a persistência localizam-se na camada de infraestrutura enquanto que as interfaces dos repositórios podem ficar no mesmo pacote dos modelos, na camada de domínio.

Baixe aqui, um exemplo de aplicação modelada com DDD.