.NET - Apresentando Hexagonal Architecture - I


  Neste artigo vou apresentar os fundamentos da arquitetura Hexagonal e apresentar um exemplo prático de uso desta arquitetura na plataforma .NET.

A arquitetura hexagonal, também conhecida como arquitetura de portas e adaptadores (Ports and Adapters architecture),  é um padrão de arquitetura de software que visa criar sistemas altamente flexíveis e desacoplados, facilitando a manutenção, testabilidade e evolução do código.

O objetivo é permitir que um aplicativo seja igualmente conduzido por usuários, programas ou testes, e que seja desenvolvido e testado isoladamente de qualquer um de seus eventuais dispositivos de tempo de execução e banco de dados.

Ela se concentra na separação de resonsabilidades e na definição clara de interfaces entre os componentes do sistema. O nome "hexagonal" é devido à sua representação gráfica, que lembra um hexágono com portas de entrada e saída em seus lados.

Ela foi sistematizada por Alistair Cockburn em 2005 como uma alternativa para de evitar as falhas estruturais conhecidas no design de software orientado a objetos, como dependências indesejadas entre camadas e contaminação do código da interface do usuário com lógica de negócios

Estabelecendo um cronograma temos que

A arquitetura Hexagonal surgiu em 2005 :


A arquitetura Cebola surgiu em 2008 :


E a arquitetura Limpa surgiu em 2012 :



Assim a arquitetura hexagonal foi a primeira dentre as arquiteturas mais tratadas atualmente.

Apresentando a arquitetura Hexagonal

A arquitetura hexagonal inicia definindo a seguinte premissa:  Todo software possui um lado interno representado pela aplicação e um lado externo que é tudo que esta fora da aplicação.



Temos assim a aplicação e o dominio contendo as regras de negócio formando o Core e o lado externo
onde podemos incluir recursos de infraestrutura e clientes que vão consumir e interagir com a aplicação e com o dominio. E para se comunicar com o lado externo na arquitetura hexagonal usamos Portas que podem ser definidas como contratos entre o lado interno (a aplicação) e o lado externo

Materializando o conceito, as portas podem ser consideradas um conjunto de interfaces e classes ou records e DTOs. As portas sozinhas não teriam muita utilidade pois precisamos acessar estas portas usando adaptadores e  os adaptadores atuam como tradutores do Mecanismo de Entrega e do cumprimento do contrato definido por uma Porta.

Fazendo uma analogia com o mundo real, temos os adaptadores para tomadas que permitem viajar pelo mundo e conectar seu aparelho a qualquer tomada mesmo quando a fonte de alimentação for diferente.  Materializando este conceito podemos considerar os Adapters como classes que implementam as interface abertas pelas Portas.

Desta forma as Portas e os adaptadores definem não apenas como o software pode acessar o armazenamento de dados ou enviar uma mensagem (conhecido como adaptadores acionados /secundários), mas também como um usuário irá interagir (API MVC Rest, Blazor, WPF, Console, etc.) com o aplicativo principal (conhecidos como adaptadores de condução/primários).Portanto, este conceito de Porta/Adaptador permite diferentes adaptadores dependendo de uma Porta.

Na arquitetura hexagonal temos duas regiões bem demarcadas :

A região de entrada (a esquerda) e a de saída ( a direita):

  1. Na região de entrada temos o que é chamado de Driving Adapters” (ou Adaptadores de Direção), que são os atores/adaptadores primários e representam os adaptadores de entrada, que na maioria dos casos podem representar a interface com o usuário. Eles conduzem a aplicação e iniciam ações nela.
    Exemplos: controladores de APIs, controladores Web ou, simplesmente, Views.
     
  2. Na região de saidas temos o que é chamado de “Driven Adapters”  (ou adaptadores acionados), que são os adaptadores/atores secundários, e representam os adaptadores de saída, ou seja, a conexão com o mundo externo. Eles reagem de acordo com as ações feitas nos adaptadores primários.
    Exemplos: conexões com banco de dados, APIs, bibliotecas, envios de Email e registros de Logs, e outros serviços,

Assim, na aplicação, temos as portas que atuam como tomadas, com interfaces bem definidas e cada região possui adaptadores que conectam o mundo externo à aplicação.

A Arquitetura Hexagonal define um fluxo em que os adaptadores de entrada dizem à aplicação o que fazer.  A aplicação, por sua vez diz aos adaptadores de saída o que eles têm que fazer, e assim,  temos então que a informação viaja da região de entrada para a de saída e de volta para a de entrada através da aplicação.

Desta forma a arquitetura Hexagonal define que as dependências devem apontar para dentro da aplicação o que é bem parecido com o que já vimos na arquitetura cebola e na arquitetura limpa.

Agora focando na aplicação que é o Core podemos destacar o modelo de dominio com as regras de negócio da aplicação , os serviços do dominio e os serviços da aplicação onde podemos definir os casos de uso orquestrando o dominio.

Principais características da arquitetura hexagonal

As principais características desta arquitetura são :

Portas (Ports): As portas são interfaces que definem os contratos entre o núcleo da aplicação e as camadas externas ou seja como o sistema se comunica com o mundo externo, incluindo interações com usuários, outros sistemas e recursos externos. Essas portas representam as entradas e saídas do sistema.

As portas definem os serviços que o núcleo precisa para funcionar, como interfaces de banco de dados, serviços web ou interfaces de usuário.

Adaptadores (Adapters): Os adaptadores são componentes que implementam as interfaces definidas nas portas. Eles traduzem as solicitações e respostas entre o sistema interno e o mundo externo e
conectam o núcleo da aplicação às camadas externas.

Os adaptadores podem incluir interfaces de usuário (UI), adaptadores de banco de dados, adaptadores de API, etc, e eles podem ser substituídos facilmente para trocar tecnologias ou implementações específicas.

Núcleo (Core): O núcleo é onde reside a lógica de negócios do sistema. Ele não está diretamente ligado às portas ou adaptadores e, portanto, pode permanecer isolado e independente de tecnologias específicas.

Entendendo a arquitetura de portas e adaptadores, podemos ver que os casos de uso geralmente devem ser escritos no limite da aplicação (o hexágono interno), para especificar as funções e eventos suportados pela aplicação, independentemente da tecnologia externa.

Benefícios da arquitetura Hexagonal

Focando em 4 aspectos principais temos a seguir os principais benefícios desta arquitetura:

1-Desacoplamento:   A separação de portas e adaptadores facilita a substituição de componentes externos, como a migração de uma interface de usuário web para uma interface de usuário móvel sem afetar a lógica de negócios central.

2-Testabilidade: A lógica de negócios está isolada no núcleo e pode ser facilmente testada sem a necessidade de interfaces externas, permitindo testes unitários e testes de unidade mais eficazes.

3-Manutenibilidade: A manutenção se torna mais simples, pois as mudanças no sistema podem ser feitas sem afetar outros componentes. Isso ajuda a evitar efeitos colaterais indesejados.

4-Adaptabilidade: A arquitetura hexagonal permite que o sistema se adapte a diferentes contextos e ambientes de execução, tornando-o mais flexível.

Essa arquitetura é especialmente útil em sistemas complexos ou em constante evolução, onde a adaptabilidade e a manutenção de longo prazo são preocupações cruciais. e promove princípios importantes de design, como o Princípio da Inversão de Dependência (Dependency Inversion Principle) e o Princípio da Responsabilidade Única (Single Responsibility Principle).

Como organizar a aplicação

Vejamos agora uma sugestão de como estruturar uma aplicação ASP.NET Core Web API com base nos componentes da arquitetura hexagonal, como mencionado anteriormente.



1- Núcleo (Core):

O "núcleo" da aplicação conterá a lógica de negócios principal, incluindo entidades, agregados, serviços de domínio e interfaces de aplicação.

2- Portas (Ports):

- Aqui podemos criar interfaces que representam as operações que o "núcleo" precisa executar. Isso pode incluir interfaces de serviço de aplicação, repositórios, serviços de infraestrutura, etc.
- Essas interfaces definem os contratos para a interação com o "núcleo".

3- Adaptadores (Adapters):

- Aqui podemos implementar as interfaces definidas nas "portas" em adaptadores específicos, que se comunicam com o mundo externo. Por exemplo:

Dependências na arquitetura hexagonal

Considerando o dominio (Domain), a infraestrutura (Infrastructure) , a aplicação (Application) e camada de interface - UI temos:



- O domínio é totalmente independente de outras camadas e frameworks;
- A aplicação depende do Domínio e é independente de frameworks, bancos de dados e UI.
- Os adaptadores fornecem implementações para as necessidades do aplicativo.
- A UI depende do Aplicativo e carrega a Infraestrutura indiretamente.


Considere que a Camada de Infraestrutura pode ter muitas responsabilidades, e, assim a recomendação é projetar a infraestrutura de forma que você possa dividi-la quando necessário, principalmente quando tiver adaptadores distintos com preocupações sobrepostas.

É importante destacar a seta tracejada da camada UI para a camada Infraestrutura que existe graças
a Injeção de Dependência que é implementada,

Conclusão

As arquiteturas Hexagonal, Limpa e Cebola compartilham a ideia fundamental de criar sistemas altamente testáveis, flexíveis e independentes de frameworks externos.

A Arquitetura Hexagonal é um bom ponto de partida para sistemas que exigem abstração de dependências (microsserviços), enquanto a Arquitetura Limpa oferece um equilíbrio entre simplicidade e independência.

A escolha entre elas depende da complexidade do projeto e das metas de abstração e independência.

Na próxima parte do artigo vamos apresentar um exemplo de implementação desta arquitetura na plataforma .NET.

"Porque virá tempo em que não suportarão a sã doutrina; mas, tendo coceira nos ouvidos, amontoarão para si doutores conforme as suas próprias concupiscências;"
2 Timóteo 4:3

Referências:


José Carlos Macoratti