Provavelmente você já sentiu a satisfação de inverter a dependência daquela classe queridinha, imaginando os mil benefícios que isso trará para o projeto, os aplausos que irá ganhar e o selo de pessoa que sabe adicionar teorias avanças ao código, mas a pergunta que surge nesse momento é:
"Faz sentido para o negócio inverter essa dependência?"
Alguns devs devem, o trocadilho não foi proposital, estar se questionando sobre o quão absurda essa pergunta pode ser. Como assim o negócio tem a ver com uma simples inversão de dependência?! É nesse ponto que as coisas começam a melhorar, ou não.
Um dos momentos mais marcantes na vida de todo dev é quando conhecemos a beleza da arquitetura de software, especialmente quando aprendemos Arquitetura Limpa e, com ela, o famoso SOLID. Saímos do livro nos achando deuses, capazes de criar qualquer sistema do mundo com essas cinco boas práticas ensinadas pelo "Uncle Bob", mas, na verdade, apenas adicionamos mais um conhecimento ao nosso repertório.
Veja, não estou demonizando nada do que está no livro; mesmo discordando de muitos pontos, claramente é uma excelente abordagem para problemas reais que provavelmente enfrentaremos. Dito isso, falemos especificamente da letra D (Dependency Inversion Principle - DIP) do SOLID, que diz:
"Módulos de alto nível não devem depender de módulos de baixo nível; ambos devem depender de abstrações (interfaces ou classes abstratas), e abstrações não devem depender de detalhes (implementações concretas)."
De forma bem resumida: devemos fazer com que nossos objetos dependam de interfaces (contratos) e não diretamente de outros objetos. Quais as vantagens? Claramente desacoplamento, testabilidade, reutilização de código e modularidade mais eficiente. No papel isso é incrível, ter a facilidade de usar qualquer adaptador, manipulando interfaces e blindando-se de dificuldades técnicas, mas e quando as complicações surgem além do código?
O DIP carrega consigo alguns problemas que fogem do software e atingem diretamente o negócio. Um dos principais é a complexidade inicial adicionada em fases embrionárias do projeto, o que piora quando é aplicado em POCs e MVPs.
Outro problema menos latente, porém complicado, é a invariância do código. Você já pegou um projeto em que ao navegar entre métodos, foi jogado para uma interface e só?! Sei que no editor você pode apertar F12 e ir para a implementação, que muitas vezes está "flutuando no limbo" de classes e não diz muito sobre a comunicação entre elas, mas já parou para pensar quanto tempo se perde com isso? Esse custo cresce exponencialmente conforme o sistema aumenta.
Essa carga cognitiva desperdiçada pode cobrar seu preço em prazos curtos para implementar algo importante e isso afeta diretamente a velocidade com que o sistema evolui junto ao negócio. Dependendo da volatilidade do segmento no qual ele está inserido, o sistema precisa mudar tão rápido que até uma simples invariância pode interferir na vantagem competitiva que depende de timing.
Além da quantidade de código acrescentada, já que você precisará criar abstrações para cada dependência, outro ponto é a maturidade técnica do time. Existe uma curva de aprendizado considerável sobre a aplicabilidade do DIP, não digo exclusivamente sobre como colocar no código, mas sim, o porque de colocá-lo. Dependendo do estágio da aplicação, esse pode ser um preço que não se paga no curto ou médio prazo.
O software deve ser apenas um meio para resolver um problema de negócio. E não entenda a palavra "problema" como um mau funcionamento ou algo relacionado, mas sim, como o diferencial competitivo que dá sentido à existência do produto como um todo. O negócio é algo vivo; portanto nosso pensamento, desde a mais simples linha de código, deve ser orientado ao problema a ser compreendido, tratado e evoluído.
A ideia desse texto vai além dos trade-offs do DIP, trata-se de levantar questionamentos antes de aplicar padrões by the book. Para não ser apenas um dev que cospe código, mas também, uma peça chave na estratégica do negócio e em sua evolução, por isso considere estas reflexões:
Tamanho do projeto
Projetos para validar hipóteses devem ser os mais simples possíveis. Inserir código desnecessário nessa fase pode mais atrapalhar do que ajudar.
- Seu projeto é grande o suficiente para que o DIP se pague no médio/longo prazo?
- Ele requer esse nível de desacoplamento em fases embrionárias?
Tipo de domínio
"Domínios genéricos e de suporte devem ser encorajados a diminuir ao máximo sua complexidade." - Vlad Khononov
Khononov afirma isso pois, geralmente, apenas os domínios principais trazem vantagem competitiva de mercado e neles devem estar concentrados toda complexidade necessária. O custo da complexidade acidental em subdomínios de suporte é muito alto em relação aos ganhos.
- Estou atuando em um domínio principal do negócio?
Conhecimento técnico do time
- O time tem conhecimento concreto sobre DIP?
- O time compreende os desafios que esse princípio traz?
- O time se sente confortável em dar manutenção em códigos que utilizam DIP extensivamente?
Acoplamento
Nem toda classe precisa dessa abordagem. Se uma classe recebe em seu construtor uma utilitária que não será usada por mais ninguém (relação 1x1), ela é uma forte candidata a não ter inversão de dependência.
- Essa classe tem, ou pode ter, uma relação 1xN?
- Ela poderá receber múltiplos clientes/implementações?
Com isso, as decisões táticas ficam mais ricas. Cada escolha importa. Estar consciente de cada passo é a melhor forma de garantir que nós, e o produto, tenhamos diferencial competitivo.
A última reflexão é uma frase potente sobre engenharia de software:
"Evite absolutos".
Evite o "sempre" e o "nunca". Nenhum software é igual ao outro; não há bala de prata, mas sim um repertório de soluções para cenários específicos. Use-o com sabedoria.
Referências bibliográficas:
MARTIN, Robert Cecil. Arquitetura Limpa (2019)
KHONONOV, Vlad. Aprenda Domain-Driven Design (2022)
Top comments (0)