Arquitetura Hexagonal é overengineering?
Construir sistemas sustentáveis não é exibicionismo técnico — é garantir que eles não entrem em queda livre no primeiro sinal de turbulência.
Recentemente, me deparei com uma publicação que acusava a Arquitetura Hexagonal de ser um exemplo clássico de overengineering. A crítica era direta: “Por que adicionar tantas camadas e complexidade, especialmente em times pequenos, com alta rotatividade e prazos apertados? Será que precisamos mesmo disso tudo?”
Esse tipo de questionamento não é novo, especialmente quando falamos de padrões arquiteturais que propõem mais estrutura. Porém, vamos pensar juntos: será que o problema está na arquitetura em si ou na forma como a aplicamos?
Na prática, overengineering raramente está ligado à escolha de uma arquitetura específica. O verdadeiro culpado é algo mais familiar para muitos de nós: camadas confusas, entrelaçadas e um design que incentiva o caos e a falta de clareza. É como construir um labirinto com um mapa inicial excelente, mas, à medida que avançamos, adicionamos caminhos sem lógica, becos sem saída e passagens que não levam a lugar algum.
No fim, ficamos presos em uma estrutura onde ninguém sabe como entrar ou sair. A culpa é do mapa original ou das nossas decisões durante a construção? O problema geralmente está em como interpretamos e aplicamos as ideias, criando uma complexidade que não reflete o propósito inicial da arquitetura.
No caso da arquitetura hexagonal, a promessa é justamente a oposta: criar um núcleo independente, organizado e testável. Então, por que tantas críticas? Vamos explorar se a arquitetura realmente complica os testes ou se é o nosso jeito de trabalhar que transforma um padrão simples em um monstro difícil de lidar.
O Que É Overengineering?
Overengineer é uma daquelas palavras que surgem com força em debates sobre tecnologia. A origem do termo está em algo simples: criar sistemas mais complexos do que o necessário para resolver um problema. É quando projetamos com um excesso de zelo, adicionando camadas, abstrações ou padrões que acabam dificultando a manutenção, a evolução e até mesmo a compreensão do código.
Mas aqui vai uma provocação: será que o problema está na arquitetura escolhida ou na forma como ela é implementada?
Overengineering Não É Sobre Arquitetura
Acusar um estilo de arquitetura, como a hexagonal, onion, ou até mesmo a escolha de um monolito, de ser “overengineering” é, muitas vezes, um sinal de falta de maturidade ou experiência. Isso porque o overengineering não está no padrão em si, mas na forma como ele é aplicado.
Pense em um monolito. Ele pode ser perfeitamente adequado para projetos grandes, com muitos módulos e regras de negócio, desde que bem projetado. O que torna um monolito um caso de overengineering não é o seu tamanho, mas como ele foi organizado:
• Se ele tem dependências cíclicas entre módulos, dificultando mudanças, isso é um problema.
• Se há lógica duplicada por toda parte, ou camadas desnecessárias que não agregam valor, aí sim podemos falar em overengineering.
Da mesma forma, um microservice mal projetado, que adiciona complexidade desnecessária para resolver um problema simples, também pode ser um exemplo clássico de overengineering.
A questão, portanto, não é o modelo escolhido, mas como ele é implementado.
A Imaturidade de Acusar Arquiteturas
Muitos desenvolvedores apressam-se em chamar arquiteturas como a Hexagonal, Onion ou Event-Driven de overengineering, mas essa crítica, na maioria dos casos, revela uma compreensão superficial de como sistemas grandes funcionam. Essas abordagens não foram criadas para dificultar a vida do programador, mas para resolver problemas reais de escalabilidade, modularidade e testabilidade.
É comum vermos programadores menos experientes (infelizmente experientes também) criticando essas arquiteturas sem considerar o contexto em que elas fazem sentido. É o equivalente a entrar em um cockpit de avião e dizer:
“Por que existem tantos botões? Isso é desnecessário!” Mas pense! A Complexidade do Avião Não Está Lá por Acaso.
Se você olhar para a imagem de um cockpit de avião comercial, verá dezenas de botões, telas, alavancas e controles. Para alguém que não entende de aviação, isso pode parecer uma complexidade desnecessária. Afinal, será que todos esses botões realmente precisam estar lá?
A resposta é sim. Cada controle no painel tem um propósito crítico – seja para garantir a segurança, facilitar ajustes em tempo real ou lidar com situações inesperadas.
Agora, imagine um programador reclamando que a Arquitetura Hexagonal pode ter algumas camadas ou que os adaptadores e portas são complexidade desnecessária. Ele está cometendo o mesmo erro que alguém que vê um botão no cockpit e acha que poderia simplesmente removê-lo sem entender suas implicações.
Arquitetura Hexagonal Não é um Painel de Botões Inúteis
No entanto, há um detalhe importante: a Arquitetura Hexagonal não é um painel de avião superlotado de botões redundantes.
• Ela não adiciona complexidade.
• Não introduz camadas arbitrárias apenas por “boas práticas”.
• Cada elemento existe para um motivo claro – manter o núcleo desacoplado, facilitar a testabilidade e permitir integrações flexíveis.
Se olharmos novamente para o cockpit, é verdade que alguns botões só serão usados em emergências, enquanto outros são operados o tempo todo. Mas todos eles existem por um motivo, porque a aviação exige segurança redundante e confiável.
No desenvolvimento de software, não precisamos da redundância extrema de um avião comercial, mas ainda precisamos de estruturas organizadas para garantir que o sistema continue funcionando bem mesmo quando as condições mudam.
O Erro da Imaturidade: Confundir Organização com Overengineering
A grande falha da imaturidade no desenvolvimento está em confundir uma arquitetura bem projetada com complexidade desnecessária.
Arquitetura Hexagonal só se torna overengineering quando:
✅ É aplicada sem necessidade (para sistemas pequenos e de vida curta).
✅ É implementada de forma errada, criando camadas sem propósito real.
✅ É usada por um time que não entende seus princípios, tornando-a um fardo em vez de uma solução.
Mas, quando usada corretamente em sistemas grandes, ela é um diferencial crítico.
Um programador maduro não apenas entende quando aplicar um padrão arquitetural, mas também por que ele foi criado.
Se um piloto precisa saber quais botões usar e quando, um bom desenvolvedor precisa saber quais padrões arquiteturais aplicar e em qual contexto.
Afinal, não é sobre a arquitetura ser complexa, mas sobre você estar preparado para pilotar o sistema do jeito certo
Overengineering na Prática
Vamos trazer isso para um exemplo prático. Imagine um sistema monolítico com várias regras de negócio e módulos interligados. Alguém pode olhar para ele e dizer: “Isso é overengineering, você deveria ter quebrado em microservices.”
Mas será mesmo?
• Se o monolito está organizado, com responsabilidades claras entre os módulos e testabilidade garantida, ele pode ser a escolha certa.
• Se for fácil adicionar novas funcionalidades ou corrigir problemas sem quebrar tudo, esse monolito está longe de ser overengineering.
Agora, por outro lado, imagine um sistema de microservices criado sem uma real necessidade. Cada serviço precisa comunicar com os outros para funcionar, há redundância de dados e operações, e o time gasta mais tempo gerenciando a infraestrutura do que desenvolvendo. Isso sim é overengineering.
Por Que Overengineering Acontece?
Overengineering acontece quando desenvolvedores ou arquitetos tomam decisões sem considerar:
1. O problema real que precisa ser resolvido.
2. O contexto do sistema e do time.
3. A capacidade de manutenção e evolução do código.
Pode vir de boas intenções – o desejo de prevenir problemas futuros ou aplicar a tecnologia mais moderna. Mas, muitas vezes, o resultado é um sistema mais difícil de entender e de usar.
Overengineering não está na escolha de um padrão ou arquitetura, mas em como o projeto é conduzido.
• Um monolito, bem projetado, pode ser mais eficiente do que um microservice mal implementado.
• Acusar princípios arquiteturais de serem “overengineering” sem compreender o problema que eles resolvem é uma demonstração de falta de experiência.
O segredo não está em evitar padrões avançados, mas em entender quando aplicá-los e como adaptá-los ao contexto. Afinal, o problema raramente é o mapa; é como construímos o caminho a partir dele.
Arquitetura Hexagonal e a Percepção de Overengineering
Antes de qualquer coisa, vamos conversar: como é a base de código que você trabalha atualmente?
Pense bem: é confusa? Clara? Talvez caótica? Você consegue dizer, com segurança, que ela é testável? Ou os testes são um pesadelo e mais parecem uma armadilha esperando para quebrar ao menor sinal de mudança?
E sobre as modificações: você sente confiança em alterar ou estender comportamentos? Ou sempre há aquele receio de que uma mudança aparentemente simples vá quebrar algo em um lugar que você nem sabia que existia?
Agora, pense nos contratos das interfaces que conectam partes do sistema. Eles são precisos e bem definidos? Ou estão em constante mudança, obrigando você a atualizar não apenas o código, mas também os testes, a cada pequeno ajuste?
Se você se identificou com alguma dessas situações, não se preocupe: você não está sozinho. Essas são dores comuns em muitas bases de código, e é exatamente nesse contexto que entra a discussão sobre a arquitetura hexagonal.
Por que a arquitetura hexagonal é acusada de ser “complexa demais”?
A arquitetura hexagonal muitas vezes é criticada como sendo overengineering por adicionar adaptadores e portas que, à primeira vista, podem parecer excessivas. Afinal, por que criar portas, adaptadores e um núcleo isolado, quando tudo poderia ser resolvido com um código direto?
A resposta, em teoria, é simples: claridade, desacoplamento e testabilidade. Mas, na prática, isso nem sempre acontece. Muitas vezes, a implementação exagerada ou mal planejada transforma um padrão arquitetural elegante em um monstro complexo.
Vamos explorar isso melhor:
1. Criar portas para tudo, mesmo quando não é necessário
• Imagine um sistema que só interage com um único banco de dados. Você realmente precisa de uma interface genérica que permita mudar de banco facilmente no futuro? Em muitos casos, não. Mas há quem implemente isso desde o início, “por precaução”, resultando em camadas adicionais que nunca serão usadas.
2. Adaptadores para cada detalhe
Adaptadores na arquitetura hexagonal conectam o núcleo ao mundo externo, mas criar um para cada pequena funcionalidade pode ser overengineering. Por exemplo, expor um endpoint apenas para retornar uma lista fixa de valores pode ser tratado diretamente no serviço ou controlador, sem a necessidade de um adaptador dedicado.
3. Excesso de mocks em testes
• Em muitos projetos hexagonais mal implementados, os testes acabam dependendo tanto de mocks que se tornam irreais. Isso pode dar a falsa sensação de segurança, mas, na prática, o sistema quebra quando interage com o mundo real. Esse problema não está na arquitetura, mas no uso desnecessário de camadas.
Sinais de que você está complicando o que deveria ser simples!
• Você está antecipando problemas que talvez nunca aconteçam. É comum tentar prever cenários futuros e criar soluções elaboradas para problemas que ainda não existem, desperdiçando tempo e energia.
• Está criando adaptadores que não são nem utilizados. Muitas vezes, adicionamos abstrações “porque podem ser úteis no futuro”, mas que acabam apenas sobrecarregando o sistema com complexidade desnecessária.
• Os testes parecem mais complicados que o código que estão testando. Um design que exige testes complexos, com diversas camadas de mocks e stubs, pode indicar que a simplicidade foi sacrificada.
• Excesso de portas e adaptadores. Quando cada funcionalidade, por menor que seja, exige seu próprio adaptador, portas específicas e estruturas adicionais, o design começa a parecer mais um labirinto do que uma solução eficaz.
• Você está usando abstrações que nem sua equipe entende direito. Se os conceitos centrais da arquitetura são tão complicados que dificultam a compreensão e o onboarding de novos membros, talvez o design precise ser repensado.
• Mudanças triviais exigem muito esforço. Alterar uma lógica simples não deveria exigir modificar múltiplas camadas, refatorar adaptadores ou reescrever testes inteiros.
• O foco está mais na arquitetura do que na entrega de valor. Quando mais tempo é gasto organizando o sistema do que resolvendo os problemas do usuário, há algo errado.
Vamos parar aqui para refletir: você reconhece algum desses problemas no seu dia a dia? Se sim, será que é a Arquitetura Hexagonal que está errada, ou é a forma como ela está sendo aplicada? Arquitetura deve ajudar a simplificar problemas complexos, mas, quando usada de forma inadequada, pode transformar soluções simples em algo desnecessariamente complicado.
Mas não termina aqui vamos analisar mais de perto outros pontos!
O Impacto nos Testes: Percepção vs. Realidade
Testar é importante, certo? E quando o assunto é arquitetura hexagonal, as opiniões se dividem ainda mais. Por um lado, temos aqueles que defendem que a separação em camadas facilita os testes. Por outro, há quem diga que ela só adiciona complexidade desnecessária, tornando os testes mais difíceis de escrever e, pior ainda, de manter. Mas será que a culpa é realmente da arquitetura? Vamos conversar.
Quando você trabalha em um sistema, como se sente ao escrever testes? É algo que flui naturalmente, ou parece que cada pequena mudança no código acaba quebrando mais testes do que deveria? Essa sensação de fragilidade é mais comum do que parece, e muitas vezes não tem a ver com a arquitetura em si, mas com como organizamos nossas camadas.
A separação entre núcleo, portas e adaptadores na arquitetura hexagonal tem um objetivo claro: garantir que o núcleo da aplicação seja isolado e testável. Em teoria, isso funciona muito bem. Os casos de uso e as regras de negócio podem ser testados sem que você precise configurar um banco de dados ou simular uma API externa. Parece perfeito, certo? Mas a prática, como sempre, é um pouco mais complicada.
Quando as Interfaces Complicam ao Invés de Ajudar
Agora, vamos falar sobre contratos – aquelas interfaces que conectam o núcleo ao mundo externo. Na teoria, elas devem ser um escudo que protege o núcleo de mudanças externas. Mas e se esse escudo for mal projetado? O que acontece?
Imagine um contrato que muda constantemente porque não foi bem pensado. A cada alteração, você precisa revisar as portas e, pior, os testes que dependem dessas portas. Isso cria uma situação em que o próprio ato de testar se torna um trabalho hercúleo. E sabe o que é mais frustrante? Muitas vezes, essas mudanças nem sequer impactam o comportamento real do sistema. Elas são, na maioria das vezes, ajustes em detalhes de implementação que acabam repercutindo em lugares onde não deveriam.
Isso acontece porque, sem um contrato claro, as dependências começam a se infiltrar onde não deveriam. Testes que deveriam estar isolados passam a quebrar por motivos que não têm nada a ver com o que eles estão validando. Nesse ponto, você começa a questionar se todo o esforço para manter essas interfaces vale a pena.
O Papel dos Adaptadores nos Testes
Adaptadores, na arquitetura hexagonal, existem para um propósito claro: manter o núcleo da aplicação limpo, isolado e desacoplado do mundo externo. Eles são responsáveis por implementar as portas, permitindo que o núcleo interaja com sistemas externos, como bancos de dados, APIs ou filas de mensagens, sem saber dos detalhes dessas dependências.
Porém, quando falamos em testar adaptadores, surge uma questão importante: será que vale a pena investir tempo e esforço para testar essas classes? A resposta depende diretamente do papel que o adaptador desempenha e da complexidade do que ele faz.
Quando NÃO vale a pena testar um adaptador diretamente
1. Quando o adaptador é uma implementação trivial do contrato (porta):
Se o adaptador apenas repassa dados entre o núcleo e o sistema externo, sem validações ou transformações, testar diretamente o adaptador pode ser desnecessário.
• Exemplo:
Um adaptador de repositório que apenas salva ou busca dados de um banco relacional. Nesse caso, um teste de contrato que valida a interface da porta já é suficiente.
2. Quando o comportamento do adaptador já está coberto por testes de integração ou end-to-end:
Se o sistema possui testes que validam o fluxo completo, do núcleo até o sistema externo (como banco de dados ou API), testar o adaptador separadamente pode ser redundante.
• Exemplo:
Testes end-to-end que verificam se os dados são corretamente salvos ou recuperados do banco já validam indiretamente o funcionamento do adaptador.
3. Quando o adaptador usa infraestrutura robusta e bem testada:
Se o adaptador interage com uma biblioteca ou tecnologia amplamente confiável (como JPA ou frameworks de mensageria), o esforço de testar o adaptador diretamente pode não agregar muito valor.
• Exemplo:
Um adaptador que usa o JPA para salvar entidades no banco. O funcionamento do JPA é confiável, e o foco pode estar nos testes do núcleo.
4. Quando o adaptador apenas delega chamadas:
Se o adaptador age como um simples encaminhador de chamadas para outro componente externo, o teste do adaptador pode ser desnecessário.
• Exemplo:
Um adaptador que delega chamadas para outro microservice sem nenhuma lógica adicional. Nesse caso, o comportamento esperado já pode ser validado por testes no serviço final.
E sobre custos e estratégias de testes?
Testar adaptadores não é gratuito. O custo vem tanto do tempo gasto para criar os testes quanto da complexidade adicionada, como a necessidade de mocks para simular dependências externas. Por isso, é fundamental avaliar:
• O adaptador realiza algo significativo ou apenas delega?
• A lógica do adaptador pode ser validada por outros testes (contratos, integração, end-to-end)?
Se o adaptador realiza transformações ou validações, talvez o foco dos testes deva estar nas classes auxiliares que realizam essas operações, e não diretamente no adaptador. Isso mantém os testes mais focados e evita redundâncias. Vamos conversar mais um pouco sobre isso?
Quando VALE a pena testar um adaptador diretamente?
Aqui vai um ponto delicado: testar adaptadores diretamente pode ser um desafio e, muitas vezes, desnecessário, dependendo do contexto. Adaptadores e portas, na arquitetura hexagonal, existem para isolar o núcleo da aplicação, e suas responsabilidades deveriam ser simples: converter dados e interagir com dependências externas. Se algo além disso está acontecendo, talvez o design precise ser revisado.
Deixe-me explicar melhor: eu, pessoalmente, sempre tive muitos problemas ao testar adaptadores diretamente, especialmente em testes de integração que acabavam sendo lentos, frágeis e, no final, pouco agregavam valor ao sistema. Pior ainda, em algumas situações, precisei recorrer a gambiarras para mockar comportamentos de serviços externos, o que só adicionou mais complexidade sem nenhum benefício real.
Como resolver isso?
• Use um caso de uso ou um serviço que realize as validações e prepare os dados para o adaptador. Assim, o adaptador recebe informações já prontas e confiáveis.
• Isso facilita a escrita de testes end-to-end (Cucumber, Cypress, etc.), que validam o fluxo completo de forma mais eficiente.
Quando você confia que os dados enviados ao adaptador estão limpos e consistentes, o foco dos testes pode ser direcionado para garantir que o sistema como um todo funcione, em vez de tentar cobrir todos os detalhes do adaptador individualmente.
Resumindo!
Se você está se perguntando qual caminho seguir, aqui vai a sugestão que funciona melhor para mim:
1. Confie nos testes end-to-end para validar o fluxo completo. Quando digo end-to-end no backend, estou falando de ferramentas como Cucumber ou Cypress, que testam todo o caminho – desde a entrada de dados até a interação com a dependência externa.
2. Evite adicionar testes de integração diretos ao adaptador sem um motivo claro. Testes lentos e frágeis, que exigem muitos mocks, raramente agregam valor.
3. Centralize a lógica de validação e transformação fora do adaptador. Use casos de uso ou serviços para garantir que os dados cheguem ao adaptador de forma limpa e confiável. Isso reduz a necessidade de testes diretos no adaptador e facilita a manutenção do código.
Adaptadores existem para deixar o núcleo limpo e protegido, mas testar diretamente esses adaptadores pode ser custoso e, na maioria das vezes, desnecessário. A chave está em manter os adaptadores simples, mover lógica complexa para serviços auxiliares e confiar nos testes end-to-end para validar o sistema como um todo.
O Equilíbrio Entre Simplicidade e Valor
A grande verdade é que testar sempre envolve decisões estratégicas. A arquitetura hexagonal, com seu foco em proteger o núcleo, oferece recursos para tornar os testes mais organizados. Mas esses recursos só funcionam bem quando usamos com inteligência. Criar adaptadores e portas para tudo, sem pensar na real necessidade, pode levar a uma avalanche de testes que, na prática, só dificultam a vida do time.
Por isso, o segredo está em equilibrar simplicidade e valor. Antes de testar um adaptador, pergunte-se: “O que eu estou realmente validando aqui?” Se a resposta for algo que já está coberto por um teste de contrato ou por um teste end-to-end, talvez você não precise investir tanto esforço nesse teste específico.
A Real Dificuldade: Cultura e Prática, Não a Arquitetura
Imagine a cena: você está no meio de um sprint, com um prazo impossível e uma funcionalidade urgente para entregar. O foco não está em fazer algo bem feito, mas em simplesmente entregar. Não importa se o código ficou cheio de gambiarras ou se os testes foram esquecidos – o que importa é que a funcionalidade está “funcionando”. Quantas vezes você já viveu isso?
Essa é a realidade de muitas empresas: a cultura do prazo a qualquer custo. Não importa o tamanho da organização – de startups a grandes corporações –, essa mentalidade cria um ciclo vicioso onde:
• A qualidade é sacrificada.
• O time se desgasta com bugs e retrabalho.
• A base de código se torna um campo minado.
Agora, vem a pergunta: será que o problema está na arquitetura que escolhemos, ou na forma como trabalhamos?
A Pressa por Resultados: Um Ciclo Autodestrutivo
Quantas vezes você já ouviu: “Não temos tempo para escrever testes agora”, ou “A arquitetura é muito complexa, só queremos algo que funcione”? A pressa por resultados cria sistemas frágeis, e o preço disso aparece na primeira instabilidade ou bug crítico em produção.
A arquitetura hexagonal não é uma bala de prata – nenhuma é. Mas ela oferece uma saída para o caos. Ela foi projetada para criar sistemas que resistem ao tempo, sistemas que podem evoluir sem se tornarem monstros incontroláveis. O problema é: sua organização está preparada para respeitar os princípios dessa arquitetura?
Alistair Cockburn, criador da arquitetura hexagonal, explicou bem o objetivo por trás dela:
“Eu inventei a ‘arquitetura hexagonal’ porque estava cansado de apagar incêndios. Então, ela acabou resolvendo de forma elegante o problema de um colega e ajudou a evitar dores de cabeça em outro projeto. Por isso, transformei-a em um padrão.”
Mas e se, ao invés de resolver incêndios, a forma como implementamos a arquitetura apenas criar mais problemas? Isso não é culpa da arquitetura – é culpa da cultura de trabalho.
A Cultura Organizacional dá prioridade para Qualidade?
Aqui está uma reflexão importante: a cultura da sua organização valoriza qualidade ou apenas quer entregar rápido, a qualquer custo?
Se a prioridade é sempre “colocar no ar”, sem considerar os riscos, você pode ter certeza de que isso vai sair caro no futuro. Bugs críticos, sistemas instáveis e retrabalho constante são o preço de uma base de código mal construída.
E as ferramentas? Ferramentas como SonarQube, Veracode e testes automatizados só fazem diferença se forem usadas. Mas quantas vezes você já viu essas ferramentas ignoradas, bypassadas ou tratadas como burocracia? Ferramentas não resolvem problemas culturais. Elas são apenas um reflexo de uma mentalidade que valoriza – ou ignora – a qualidade.
A pergunta que programadores e arquitetos deveriam fazer é: estamos comunicando os riscos para as pessoas de negócio?
Se o time técnico não deixa claro que falta de padrões e qualidade custa caro, as áreas de negócio vão continuar pedindo entregas rápidas sem entender as consequências. E quem paga o preço disso? O time técnico – com mais bugs, mais trabalho, mais horas extras.
A Cultura de Testes: Só o “Caminho Feliz” Não Basta
Um dos maiores reflexos da pressa é a falta de cultura de testes. Quando falamos de testes, é comum encontrar:
• Testes feitos apenas para o “caminho feliz”, ignorando cenários de erro e edge cases.
• Testes escritos sem padrão, onde cada desenvolvedor usa uma abordagem diferente.
• Equipes que não aproveitam as ferramentas que o próprio framework oferece.
Agora pense: como você confia no seu sistema se ele só foi testado no cenário ideal? Quando algo inesperado acontece – como um erro de API, uma resposta inválida, ou um pico de usuários – o que acontece? O sistema vai continuar a se comportar de maneira previsível? Sem testes bem escritos e buscando além do caminho feliz você não pode garantir isso!
A arquitetura hexagonal nos dá a oportunidade de escrever testes de qualidade:
Casos de uso são fáceis de testar isoladamente. A lógica de negócios pode ser validada sem dependências externas. As regras quando estão protegidas ficam fáceis de testar e validar!
Testes end-to-end complementam a visão geral. Ferramentas como Cucumber e Cypress (backend) ajudam a validar que o sistema inteiro funciona como esperado.
Mas testes só funcionam se houver uma cultura que os valorize. Escrever testes é investimento, não desperdício de tempo. A questão é: a sua organização entende isso?
Arquitetura Hexagonal: Um Fardo ou Uma Solução?
Alistair Cockburn deixou claro: a arquitetura hexagonal foi criada para nos tirar do caos, não para complicar nossas vidas.
“A arquitetura hexagonal é uma maneira de manter o núcleo limpo, não de complicar a vida dos desenvolvedores.”
Se a arquitetura parece difícil ou “desnecessariamente complexa”, talvez seja porque:
1. O time não entende seus princípios: Sem treinamento adequado, a arquitetura hexagonal pode parecer um bicho de sete cabeças.
2. Ela está sendo aplicada de forma errada: Regras de negócio no adaptador? Contratos mal definidos? Isso não é culpa da arquitetura, mas de como ela foi implementada.
A arquitetura hexagonal não é um fardo – ela é um guia para criar sistemas sustentáveis. Mas para isso, é preciso respeitar suas regras e princípios.
Como Mudar Essa Cultura?
Aqui está o ponto principal: a verdadeira dificuldade não está na arquitetura, mas na cultura e na prática diária do time. Para adotar a arquitetura hexagonal de forma eficaz, é preciso:
• Onboarding claro e bem planejado: O time precisa entender como a arquitetura funciona e por que ela foi escolhida.
• Documentação acessível: Escreva guias que ajudem os desenvolvedores a seguir os padrões, com exemplos reais e práticos.
• Comunicação com o negócio: Explique os riscos de sistemas mal projetados. Mostre que qualidade não é opcional – é uma forma de economizar no longo prazo.
• Incentivo ao uso correto de ferramentas: SonarQube e Veracode só fazem diferença se forem usados para melhorar a qualidade, e não como burocracia ou algo a ser ignorado.
A arquitetura hexagonal não carrega consigo complexidade desnecessária. Ela é uma ferramenta poderosa para organizar o caos, mas não resolve problemas culturais. Se a sua organização valoriza apenas prazos e funcionalidades, sem construir alicerces sólidos, o sistema vai durar pouco tempo sem causar problemas e dores de cabeça em produção.
Por Que Precisamos de um Núcleo Descentralizado?
Vamos conversar sobre algo fundamental: a centralização das regras de negócio e a proteção do núcleo em projetos grandes. Quantas vezes você já viu uma base de código onde as regras de negócio estão espalhadas por várias camadas, adaptadores ou até diretamente nos controladores? Não é difícil encontrar sistemas assim, especialmente em arquiteturas mais tradicionais como MVC ou até mesmo na Onion Architecture, que, à primeira vista, parecem resolver tudo, mas podem falhar quando o projeto cresce.
Agora, vamos explorar juntos por que é tão difícil descentralizar, proteger o núcleo e fazer isso de maneira sustentável.
O Problema com MVC em Projetos Grandes
MVC é um padrão popular, mas ele não foi feito para escalar indefinidamente. A ideia básica é simples:
• Model: Lida com os dados.
• View: Cuida da interface com o usuário.
• Controller: Faz a ponte entre os dois.
Até aí, parece ótimo. Mas o que acontece quando o sistema começa a crescer? Quando as regras de negócio precisam se conectar a múltiplos adaptadores, ou quando cada funcionalidade exige mudanças em várias partes do código? O MVC começa a mostrar suas limitações:
• Acoplamento: É comum ver regras de negócio sendo empurradas para os controladores. Isso cria um vínculo perigoso entre a lógica do sistema e a forma como os dados são apresentados ou recebidos.
• Dificuldade de Testes: Quando os controladores começam a acumular responsabilidades, testar cada cenário de maneira isolada se torna quase impossível.
• Manutenção Dolorosa: Você adiciona uma nova funcionalidade, mas precisa atualizar o modelo, o controlador e, às vezes, até mesmo a view. O tempo gasto aumenta exponencialmente.
MVC é eficiente para projetos pequenos ou médios, mas, em sistemas grandes, ele pode levar a um estado de “tudo depende de tudo”, o que é o oposto do que queremos.
E Quanto à Onion Architecture?
A Onion Architecture é um avanço em relação ao MVC porque introduz camadas claras ao redor do núcleo da aplicação. A ideia central é que o núcleo deve ser independente de tudo o mais, e as dependências externas (como bancos de dados e APIs) ficam nas camadas mais externas. Isso resolve muitos problemas de acoplamento que vemos no MVC.
Mas será que ela funciona bem para projetos grandes? Depende.
• Regras Espalhadas:
Um dos problemas da Onion Architecture é que, mesmo com camadas organizadas, as regras de negócio podem acabar espalhadas entre o núcleo e outras camadas. Isso acontece quando o design do núcleo não é forte o suficiente para manter tudo isolado.
• Dificuldade em Adicionar Comportamentos:
Em projetos grandes, é comum que novos comportamentos precisem interagir com várias partes do sistema. Se a Onion Architecture não for bem implementada, você acaba tendo que atravessar várias camadas para fazer pequenas mudanças, o que adiciona complexidade.
• Risco de Dependências Invisíveis:
Embora a Onion proponha independência do núcleo, na prática, equipes às vezes introduzem dependências implícitas entre camadas, especialmente se os contratos não forem bem definidos. Isso cria um falso senso de segurança.
Por Que a Descentralização do Núcleo é Difícil?
Agora, vamos ao ponto principal: descentralizar e proteger o núcleo é uma tarefa difícil porque vai contra hábitos comuns de desenvolvimento.
• Muitas vezes, priorizamos a entrega rápida sobre a organização.
• Desenvolvedores acabam colocando “só uma validação” aqui, “só uma transformação” ali, e, de repente, o núcleo está cheio de detalhes técnicos que ele nunca deveria conhecer.
• Outro desafio é cultural: nem sempre os times entendem ou respeitam a ideia de que o núcleo deve ser independente e isolado.
Para proteger o núcleo, precisamos mudar a forma como pensamos. Em vez de adicionar lógica onde for mais conveniente, devemos perguntar: “Onde isso realmente pertence?” Isso exige disciplina, e é aí que muitas equipes falham.
Por Que a Arquitetura Hexagonal Ajuda?
A arquitetura hexagonal resolve muitos desses problemas porque ela faz uma separação clara entre o núcleo e o restante do sistema. Ela não apenas organiza as dependências externas, mas também cria uma estrutura onde as regras de negócio estão no centro de tudo – literalmente.
Alistair Cockburn, criador da arquitetura hexagonal, explicou assim:
“A arquitetura hexagonal é uma maneira de isolar o núcleo do caos externo. É como criar uma ilha segura onde suas regras de negócio podem viver sem interferências.”
Isso significa que:
• O núcleo não sabe de nada externo: Ele não sabe se os dados vêm de uma API, de um banco de dados ou de um arquivo CSV. Ele apenas define como as coisas devem funcionar.
• As portas são contratos claros: Elas garantem que qualquer coisa que queira interagir com o núcleo deve seguir suas regras.
• Os adaptadores fazem o trabalho pesado: Eles convertem os dados do mundo externo para o formato que o núcleo entende e vice-versa.
A Diferença em Projetos Grandes
Em projetos pequenos, talvez a diferença entre MVC, Onion e Hexagonal Architecture não seja tão evidente. Mas em projetos grandes, a arquitetura hexagonal brilha porque:
• Ela reduz o acoplamento: Cada parte do sistema tem uma responsabilidade clara. Isso facilita mudanças e ampliações.
• Ela centraliza as regras: Tudo que é importante para o negócio está no núcleo, protegido de interferências externas.
• Ela facilita os testes: Você pode testar o núcleo isoladamente, sem se preocupar com detalhes técnicos.
Proteger o núcleo e descentralizá-lo é essencial para sistemas grandes e complexos. MVC e Onion Architecture têm seus méritos, mas nem sempre conseguem lidar com a escala e a complexidade.
Nem Todo Sistema Precisa de Uma Arquitetura Hexagonal
Imagine que você está começando um pequeno projeto ou algo com um ciclo de vida curto, como um protótipo ou MVP (Minimum Viable Product). A pressão por entrega é alta, o escopo é claro, e as funcionalidades são bem definidas. Será que vale a pena investir tempo e energia em uma estrutura complexa como a arquitetura hexagonal?
A verdade é que, em sistemas pequenos ou com vida útil limitada, a arquitetura hexagonal pode não oferecer benefícios suficientes para justificar o esforço.
Por exemplo, se você está construindo um aplicativo que terá apenas uma integração com banco de dados e nenhuma complexidade adicional, talvez uma abordagem mais direta seja suficiente. Por que criar portas, adaptadores e toda a infraestrutura para algo que não será mantido ou evoluído?
Não se trata de negligenciar qualidade, mas de saber escolher o nível de abstração certo para o contexto.
E Quando o Projeto é Simples?
Agora, pense em um sistema com uma única funcionalidade e um fluxo linear. Imagine algo como um gerenciador de tarefas para um pequeno time, onde as interações são simples: criar, editar, excluir e listar.
Se o sistema não tem integrações complexas ou necessidade de suportar múltiplos tipos de entrada e saída, aplicar a arquitetura hexagonal pode parecer como usar um canhão para matar uma formiga. Você estaria adicionando complexidade onde não é necessário.
Isso não significa que o sistema deva ser mal projetado. Mesmo em projetos simples, boas práticas como separação de responsabilidades e testes devem ser mantidas. Mas criar camadas desnecessárias para um sistema que nunca precisará dessas abstrações pode ser um desperdício.
Quando a Experiência do Time Pode Ser um Obstáculo
Outro ponto que precisa ser considerado é a experiência do time. A arquitetura hexagonal exige entendimento, disciplina e um certo nível de maturidade no desenvolvimento. Se o time não está familiarizado com seus princípios ou se a organização não tem tempo para treinar a equipe, aplicar a arquitetura pode levar a resultados ruins.
Já vi casos em que as portas eram criadas sem contratos claros, os adaptadores acumulavam lógica de negócio, e o núcleo, que deveria estar isolado, acabava cheio de dependências externas. Isso não é culpa da arquitetura em si, mas da forma como foi implementada e por decisões precipitadas!
Se o seu time não está preparado tecnicamente ou o projeto está em ambiente produtivo com uma forte pressão de prazo (podemos discutir isso em outro artigo também), talvez seja sábio adotar uma abordagem mais simples até que haja tempo para maturar a prática.
E os Custos da Complexidade?
Todo sistema tem um custo de complexidade. Quanto maior a abstração, mais esforço é necessário para projetar, implementar e manter o código. A arquitetura hexagonal brilha em sistemas que precisam escalar, que terão muitas integrações externas, ou que devem proteger suas regras valiosas que são o motor da aplicação.
Mas se o seu sistema não tem essas necessidades, os custos podem superar os benefícios. Pense em uma aplicação monolítica simples, que não depende de APIs externas complexas ou múltiplos bancos de dados. Será que vale a pena criar portas e adaptadores? Provavelmente não.
O importante aqui é pesar os prós e contras. Se você não vê um ganho claro a médio ou longo prazo, talvez a arquitetura hexagonal não seja a melhor escolha para aquele projeto.
Conclusão
A arquitetura hexagonal não é um monstro de sete cabeças, mas também não é um passe de mágica. Ela resolve muitos problemas de acoplamento e escalabilidade, mas só funciona bem se aplicada do jeito certo. Afinal, de nada adianta um padrão arquitetural sofisticado se ele for implementado sem propósito ou sem o devido entendimento.
Se você sente que a arquitetura só trouxe mais complexidade ao seu projeto, vale a pena se perguntar: será que o problema está na arquitetura ou na forma como ela foi implementada? Ou, indo além: será que ela era realmente necessária para esse projeto? Muitas vezes, a dor que tentamos resolver com uma abordagem sofisticada poderia ser resolvida com algo mais simples e direto.
E aí está o ponto essencial: nem todo sistema precisa de uma arquitetura robusta. Para projetos pequenos e bem delimitados, adicionar camadas de abstração pode ser um desperdício de tempo e recursos. Mas quando falamos de aplicações grandes, que precisam escalar, suportar múltiplos pontos de integração ou se manter flexíveis para mudanças futuras, a arquitetura hexagonal pode ser um divisor de águas.
A melhor arquitetura não é aquela que segue um padrão famoso ou que parece moderna, mas sim aquela que se encaixa na realidade do projeto. Adotar a arquitetura hexagonal só porque é tendência ou porque um arquiteto disse que é “a melhor solução” pode ser um erro tão grande quanto ignorar boas práticas de design de software. O contexto sempre deve guiar a decisão.
Além disso, arquitetura não é apenas código — é cultura. Se o time não entende os princípios por trás do modelo adotado, os benefícios se perdem no caminho. Por isso, investir em um onboarding bem estruturado, criar documentação acessível e reforçar boas práticas no dia a dia são tão importantes quanto escolher a arquitetura certa.
No fim das contas, o verdadeiro desafio não está apenas na escolha entre MVC, Onion ou Hexagonal. Está na maturidade de saber quando e como aplicar cada abordagem. Arquitetura não é sobre seguir modas, é sobre resolver problemas reais de forma sustentável.