O que é arquitetura hexagonal

Nesse primeiro post sobre arquitetura de software, vamos falar um pouco sobre a arquitetura hexagonal. Proposta em 2005 por Alistair Cockburn, a arquitetura hexagonal visa um projeto com:

  • Separação de responsabilidades: cada componente tem sua responsabilidade bem definida.
  • Foco na regra de negócio: a separação de camadas e componentes, proporciona um melhor detalhamento e foco na regra de negócio da aplicação.
  • Paralelização de trabalho: como a arquitetura hexagonal define muito bem as responsabilidades de cada componente, é possível paralelizar trabalho facilmente.
  • Isolamento de testes: devido a baixa dependência entre componente, escrever testes de qualidade, se torna uma tarefa muito mais simples.
  • Mudança de infraestrutura: como existe uma separação entre a regra de negócio e a camada que se comunica com o mundo exterior, mudar do MySQL para Postgres é menos doloroso do que em aplicações que não seguem a arquitetura hexagonal.

Em outras palavras, utilizar a arquitetura hexagonal faz com que um projeto seja escalável, de fácil manutenção e produtivo na hora de ser implementado.

Agora vamos entender os termos e camadas da arquitetura hexagonal.

Core

Na arquitetura hexagonal, tudo é construído ao redor do core. Pense nele como uma caixa – representada por um hexágono -, que como você já deve imaginar, é onde fica a regra de negócio.

Tudo o que não for relacionado com a regra de negócio da aplicação, ou seja, tudo aquilo que estiver fora desse hexágono, é visto como “mundo externo”.

Antes de falar sobre o “mundo externo”, precisamos ter muito claro que, quando estamos planejando o core da aplicação, devemos nos abster de decisões técnicas, como por exemplo:

  • Que banco de dados utilizar?
  • Como será a relação entre as tabelas?
  • Vamos utilizar um framework ou algum ORM?
  • A API será REST ou gRPC?

O core da aplicação, onde a regra de negócio é implementada, deve ser 100% agnóstica a qualquer uma dessas decisões, focando 100% em resolver o problema para o qual a aplicação está nascendo para resolver.

Atores

Atores são “coisas” do mundo externo ao core. Tais “coisas” podem ser pessoas, bancos de dados ou até mesmo outras aplicações.

Para ficar mais claro como cada ator interage com o core da aplicação, vamos separá-los em dois grupos.

  • Drivers: são atores que disparam alguma interação com o core da aplicação. Pessoas são ótimos exemplos de atores do tipo drivers.
  • Driven: são atores que esperam uma comunicação vinda do core, ou seja, ao contrário dos atores do tipo drivers, aqui o core é responsável por iniciar a interação. Bancos de dados, sistemas de fila e outras APIs são exemplos de atores do tipo driven.

Como os atores pertecem ao mundo externo, o core não deve estar diretamente conectado a nenhum deles, ou seja, nenhuma lógica relacionada aos atores deve fazer parte do core. Da mesma forma, nenhuma lógica relacionada ao core deve fazer parte dos componentes que o conectam ao mundo externo.

Ok… Provavelmente agora você está se perguntando algo como, “mas então como fazemos para que esses componentes se comuniquem?”

Simples! Utilizando um conceito chamado de Ports & Adapters.

Ports

São interfaces definidas no core para comunicação com o mundo externo. Assim como os atores, existem dois tipos de ports.

  • Ports para drivers: são ações expostas ao atores do tipo drivers através de funções e métodos.
  • Ports para driven: são interfaces de comunicação bem definidas, que deverão ser implementadas pelos atores do tipo driven.

Adapters

São componentes responsáveis pela integração do core ao mundo externo. Em outras palavras, o papel dos adapters é traduzir o que o ator do tipo driver quer, para o que o core entende, assim como o que o core quer, para o que ator do tipo driven entende.

Se ficou confuso o parágrafo acima, pense assim.

  • Adapters para drivers: traduzem requests em chamadas de serviço do core.
  • Adapters para driven: traduzem a solicitação do core em algo que seu ator entenderá, como por exemplo, SQL.

Show! Mas como eu faço para não criar dependência entre o core e seus adapters para atores do tipo driven?

Dependency Injection

Para não criar nenhuma dependência entre o core da aplicação e seus adapters, devemos utilizar a técnica de dependency injection.

Se você não está familiarizado com essa técnica, ela consistem em utilizar interfaces como parâmetros de funções e métodos, ao invés de importar diretamente determinados componentes.

Quando uma função ou método é chamado, o componente que implementa aquela interface é passado, ou seja, injetado via parâmetro.

Essa técnica também facilita a escrita de testes, já que você pode, por exemplo, implementar um banco de dados em memória e injetá-lo nos testes do core. Dessa forma, focamos os testes do core nas regras de negócio da aplicação e removemos qualquer ruído da comunicação com o banco de dados real.

Conclusão

Esse tipo de arquitetura não é recomendada para pequenas aplicações, pois a complexidade adicionada na analise e separação dos componentes não deve trazer grandes ganhos.

No caso de grandes aplicações, tal complexidade é justamente o que irá salvar horas e mais horas do time de desenvolvimento na hora de, implementar uma nova feature, adicionar testes e melhorar a qualidade geral da aplicação.

Se você quiser ver como eu recomendo a organização de pastas em um projeto Go que implemente a arquitetura hexagonal, assista ao vídeo que publicamos lá no canal do YouTube.

Até a próxima!


Se inscreva na nossa newsletter

* indicates required

3 comentários sobre “O que é arquitetura hexagonal

Deixe uma resposta