Fala-se sobre Código Limpo há algum tempo, sobre funcionalidades que melhoram a legibilidade do código e bibliotecas que agilizam e deixam o código menor e, às vezes, melhor. Mas há questões bem mais fundamentais que não podem ser esquecidas ou despriorizadas em relação àqueles assuntos mais populares, entre eles, a nomenclatura de tudo o que está sendo escrito.

Quando precisamos realizar alterações em um sistema já desenvolvido, mas desconhecido, temos a necessidade de avaliar, como um todo, quais são suas funcionalidades. Certas vezes nem mesmo é desconhecido, mas trabalhado há alguns meses e com detalhes específicos de arquitetura para relembrar. Esse sistema provavelmente está em produção e quebrar algo em funcionamento não é uma possibilidade.

Num primeiro momento pouco importam as bibliotecas utilizadas, a versão da linguagem ou mesmo detalhes do ambiente de execução. A primeira coisa que devemos observar no sistema para saber qual é o seu domínio de negócio são suas classes e pacotes. Depois, olhamos para seus atributos e métodos, a fim de entender os recursos que cada parte do domínio tem. Em terceiro, analisamos os testes, que irão dizer como os recursos devem se comportar. Somente após isso, pode-se passar aos detalhes da tecnologia, versões, ambientes e bibliotecas.

Guarde esta ordem:

  • Classes e seus pacotes
  • Atributos e Métodos
  • Testes

Numa aplicação em que não puder ser obtida uma visão geral seguindo os passos mencionados, certamente não é orientada a objetos, mesmo que use uma linguagem dita como OO. Não conhece Orientação a Objetos mas está lendo este artigo? Que bom, é um ótimo começo!

Por que os pacotes estão no primeiro item?

Os pacotes estão juntos às classes para sustentá-las. Pouco importa ter um pacote se não existir uma classe dentro deste. Porém, caso seja ignorada a sua importância, deixando as classes mal distribuídas, tornará muito difícil encontrar a sua localização. Sem falar que se o sistema crescer, irá virar uma bagunça.

Há quem considere a divisão dos pacotes a parte mais simples, afirmando que são sempre os mesmos: entidade, serviço, dao, util, rest… Outros discordarão com veemência dizendo que preferem repositorio à dao e controlador à rest. Algum deles está correto? De certa forma, sim. Esta quebra por camadas é muito utilizada, e comum para quem está começando a trabalhar com MVC. É uma abordagem sem grandes dúvidas: em pouco tempo está definida para toda a vida do projeto e o restante do tempo fica com a codificação.

Agora, trago a verdade sobre os pacotes. Essa divisão simples por camadas é como colocar uma caixa escrito “gavetas” na sua garagem e juntar todas as gavetas da casa ali dentro: gavetas da cozinha, do guarda-roupas, do armário de produtos de limpeza. Faz sentido? Claro, deixamos todas as gavetas, que possuem a mesma finalidade juntas, não é verdade? Mas na prática isso tem pouco sentido, principalmente em sistemas maiores, onde temos dezenas de classes na mesma camada. Por que faríamos diferente com o nosso software? Faz muito mais sentido deixar coisas onde realmente são utilizadas do que juntá-las no mesmo local.

Mas como seria essa organização? Seguindo o mesmo exemplo, pense nos cômodos da casa como pacotes e nos compartimentos dos cômodos como subpacotes. Dentro deles estarão as ferramentas ou utensílios, ou seja, as classes. Então:

  • Cozinha
    • Armário
      • Gaveta
        • Talheres
      • Balcão
    • Fogão
  • Sala
  • Quarto
    • Guarda-roupas
      • Gaveta
        • Meias

Não achou que poderia organizar sua casa em pacotes, subpacotes e classes? Pois é. A orientação a objetos é a melhor forma pra isso, trazer abstrações do mundo real para dentro de um sistema, de forma que se alguém pedir para adicionar uma funcionalidade de “bancada” no armário da cozinha, saberemos exatamente onde criar o pacote e suas classes. Não é uma forma fácil e vai gerar muitas dúvidas, mas fica mais próximo da realidade, o que é bom para a Orientação a Objetos.

Certo… E as classes?

Pois bem, já definimos os pacotes e agora podemos pensar de fato nas classes. Utilizando a abordagem de divisão por lógica que vimos a pouco, sabemos que devemos deixar as classes relacionadas próximas. Dentre estas, as mais importantes do nosso sistema são àquelas do domínio do negócio, as quais contém características, estado e ações. Classes como Aluno, Turma, Cliente, Animal, Pagamento são exemplos disso.

Essas classes do domínio do negócio, conhecidas como modelo, também podem se tornar entidades, se forem relacionadas a um banco de dados. Eventualmente estas entidades precisam ser listadas, criadas, apagadas ou atualizadas, sendo que neste caso delegamos a uma classe de persistência, normalmente chamada de repositório ou dao. Além da própria entidade e seu repositório, ainda é comum o uso de uma classe onde as regras de negócio entre diferentes entidades são relacionadas, chamadas de serviço.

Assim, temos três classes intrinsecamente relacionadas: a Entidade, seu Repositório e seu Serviço. Com muita dificuldade conseguiremos separá-las, razão pela qual devem permanecer no mesmo pacote.

Ainda, as classes de Serviço podem vir a ficar sobrecarregadas devido à quantidade de regras e operações que algumas entidades tem. Logo, para melhorar a manutenção deste código, devem ser decompostas em classes específicas, reduzindo a complexidade e organizando as responsabilidades. Por exemplo, em um sistema de Petshop, poderíamos ter um pacote animal, com as classes Animal, AnimalRepositorio, AnimalService, AnimalAlimentacaoServicoe AnimalSaudeServico. Isso, considerando que a “alimentação” e “saúde” dos animais possuem muitas regras que justifiquem a divisão. O uso da nomenclatura em inglês também é comum, contudo não é relevante para este artigo, desde que você adote um padrão: service e repository.

Só estas classes compõe uma aplicação?

De fato não conseguimos fazer todo o necessário somente com estas classes, pois nossos modelos quase sempre precisam conversar com o mundo externo. Para isso, serão necessários Controllers, Clientes de APIs, Filas, etc. Essas classes fazem parte de uma aplicação, mas não são parte do “negócio”, então considere separar esses recursos tecnológicos em algum pacote específico como api, infra, cliente, etc…

Além disso, precisamos de utilitários que nos ajudam a manter a abstração dentro de nossos modelos, e também são aceitos. Eles normalmente são conhecidos como Builders, Factories, Converters, Mappers, etc. Mas pense que o objetivo destes é simplificar a lógica das classes de negócio, apenas isso.

Ok, organizamos nosso negócio em pacotes e classes, mas onde de fato ficam nossas regras e informações?

Nesse momento entram os atributos de nossas classes para armazenar as informações, características e estado. E os métodos para realizar as ações, mudanças de estado e validações. Da mesma forma que os pacotes e classes devem ser precisamente pensados para dar sentido à nossa organização.

Os atributos devem parecer com características ou estado. Devem fazer sentido para o negócio e não conter prefixos ou sufixos tecnológicos que não ajudam na abstração. Ou seja, prefira “tamanho” à “nroTamanho”, por exemplo.

Quanto aos métodos, também devem parecer ações do negócio e mudança de estado e, ainda, evitar prefixos e sufixos que não auxiliam na compreensão do negócio. Caso sejam necessárias conversões, cópia de objetos ou quaisquer outras ações que não fazem parte do comportamento natural do negócio, considere criar as classes que citamos anteriormente, como Converters, Mappers, Builders, Factories

Testes?!

Bem, como último dentre os itens citados, mas não menos relevante, temos os testes. Sua posição deve-se unicamente pela ordem natural de entender a visão geral da arquitetura, o contexto de cada uma das funcionalidades e aí sim adentrar nos detalhes da implementação.

Chegada esta etapa podemos interpretar de fato o que o sistema poderia e deveria fazer, e os testes são bons para nos contar isso: além dos cenários principais os testes nos mostram os secundários e os de exceção, com a reação prevista do software quando nem tudo sai conforme gostaríamos. Esse tipo de informação é difícil de extrair de alguns códigos de produção ou implícitos em outros, mas os testes tendem a deixá-los explícitos, o que facilita o trabalho de manutenção.

Encerra-se por aqui a discussão? Com certeza não! Existem diversas abordagens de organização e arquitetura. Aqui trago uma visão subjetiva do que considero importante após alguns anos de programação, objetivando que seja dada maior ênfase à abstração do negócio, que é o motivador para a criação dos sistemas, em vez da tecnologia, bibliotecas e termos que afastam o propósito real do código.

Também não foi o objetivo falar especificamente sobre uma ou outra arquitetura, mas há muito material disponível. Se você ficou motivado, considere pesquisar sobre DDD e Arquitetura Hexagonal.