Quando fazer o “aparentemente errado” é certo

Um dos maiores desafios à manutenabilidade é o alto acoplamento. Entretanto, muitas vezes, tentativas ingênuas de combatê-lo levam ao aumento desnecessário de complexidade, implicando, exatamente, no aumento dos custos de manutenção.

É comum, por exemplo, encontrarmos sistemas, sobretudo escritos em linguagens orientadas a objetos fortemente tipadas, como C# e Java, com cuidado demasiado na separação das responsabilidades.

Não é incomum encontrar “soluções” com, pelo menos, um projeto para a apresentação, outro para o domínio e um terceiro para infraestrutura. Aliás, é bem comum encontrar soluções ainda mais fragmentadas.

Geralmente, o objetivo é impedir que referências de um projeto “vazem” para os demais. Entretanto, para aplicações pequenas ou para microsserviços, esse excesso de zelo gera bases de códigos maiores que não agregam valor.

Não são raras as implementações genéricas, por exemplo, de repositórios que apenas “mascaram” o acesso a base de dados mas que são absolutamente anêmicas.

public interface IRepository<T>
{
  T GetById(string id)
  IQueryable<T> GetAll();
  void Save(T object);
  // ..
}

Ainda piores são implementações, extremamente extensas, que ignoram o fato de que repositórios são apenas um dos diversos tipos de serviços associados ao domínio, tornando-os pedágios caros para a realização de consultas, muitas vezes “hidratando” objetos pesados, como entidades com dezenas de propriedades, que são quase completamente descartados, na conversão para DTOs com dois ou três campos para composição de respostas da aplicação para os “clientes”. O mais simples seria uma consulta econômica, geralmente não realizada em nome das “boas práticas”.

Também são muito comuns os casos onde se leva absolutamente a risca a “regra” de não implementar lógica em bases de dados, condenando aplicações a trafegar dados em demasia na rede sem nenhuma compensação evidente. Em cenários onde performance seja uma demanda crítica, muitas vezes, utilizar mecanismos de indexação ou pré-computação na base podem “resolver o problema” de maneira muito mais eficaz do que trazendo o processamento “na unha” no código (frequentemente recorrendo a estratégias questionáveis de caching).

Há, finalmente, quem insista em criar “telefones sem fio” fazendo o tratamento de requisições “passar” pelas diversas camadas sem que nenhum processamento real aconteça ou, ainda pior, aplicando mapeamentos entre DTOs e ViewModels. O resultado é apenas uma stack de processamento maior, que aumenta tempos de resposta, muitas vezes criando objetos de maneira desnecessária, estressando o Garbage Collector. Idealmente, tais requisições deveriam ser tratadas de maneira muito mais econômica, trazendo, eventualmente, acesso a dados a camada de aplicação, tornando o código mais simples de entender e, geralmente, causando melhorias de desempenho.

A existência de camadas destinadas apenas a transferência de objetos, sem lógica ou valor de negócios, gera a necessidade de codificar testes repetitivos e sem sentido para manter o alto índice de cobertura. Isso pode tornar a tarefa de escrever testes mais morosa e acabar desestimulando os desenvolvedores a escrevê-los.

A obsessão por patterns e técnicas sofisticadas de abstração são, com frequência, indicativos da falta de entendimento quanto a conceitos básicos relacionados a programação orientada-a-objetos. Esforços para isolar a infraestrutura na camada de domínio, em alguns cenários, acabam mitigando uma “gravidade artificial” que seria plenamente aceitável caso houvesse entendimento das consequências dessa decisão.

Alguns bancos NoSQL, como o RavenDB, possibilitam que o modelo de domínio seja persistido com pouca ou nenhuma adaptação de estrutura. Bancos de dados relacionais, por outro lado, são incompatíveis com o “modelo de domínio perfeito”. Em função disso, a solução tida como a mais “correta” seria escrever um modelo específico para persistência. Entretanto, em projetos pequenos, os benefícios dessa “solução caprichosa”, talvez não fiquem tão perceptíveis. Algumas vezes, é mais barato fazer pequenas adaptações no modelo de domínio mesmo que algum “vazamento” da infraestrutura aconteça.

Não raro, as soluções mais baratas de desenvolver e manter são também as mais simples. Ignorar isso é expressão de um preciosismo técnico ou de total falta de entendimento do que significa a aplicação de boas práticas.

Em Resumo
  • O problema

    Um dos maiores desafios à manutenabilidade é o alto acoplamento. Entretanto, muitas vezes, tentativas ingênuas de combatê-lo levam ao aumento desnecessário de complexidade, implicando, exatamente, no aumento dos custos de manutenção.
  • O insight

    Algumas concessões técnicas, aparentemente questionáveis, quando bem justificadas, reduzem a complexidade. Apesar de gerar algum acoplamento, diminuem custos e, consequentemente, geram maior valor percebido para o negócio.

Elemar Júnior

Microsoft Regional Director e Microsoft MVP. Atua, há mais de duas décadas, desenvolvendo software e negócios digitais de classe mundial. Teve o privilégio de ajudar a mudar a forma como o Brasil vende, projeta e produz móveis através de software. Hoje, seus interesses técnicos são arquiteturas escaláveis. bancos de dados e ferramentas de integração. Além disso, é fascinado por estratégia e organizações exponenciais.

Grazi Bonizi

Especialista em DevOps, auxilia empresas em seu processo de transformação digital. Ativa na comunidade, coordena há três anos a trilha de arquitetura .NET no TDC São Paulo, além de palestrar e escrever em diversos canais, normalmente sobre DevOps, Azure, .NET, Docker, Kubernetes e DDD.

Talvez você goste também

Carregando posts…
1 comentário
  1. Patrick

    Eu poderia resumir este artigo dizendo que é necessário ter bom senso na modelagem do sistema, observando a necessidade da implementação das camadas e objetos que são trafegados pela stack..

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *