Article image
João Rafael
João Rafael18/07/2024 16:47
Compartilhe

Arquitetura Hexagonal em Projetos Go (Golang)

  • #GoLang

Arquitetura Hexagonal em Projetos Go (Golang)

A arquitetura hexagonal, também conhecida como arquitetura de portas e adaptadores, é um padrão de design que promove a modularidade e a testabilidade em sistemas de software. Essa abordagem busca isolar o núcleo da lógica de negócios do restante do sistema, facilitando a manutenção, a evolução e a testagem automatizada. Ao aplicar a arquitetura hexagonal a projetos em Go (Golang), podemos aproveitar os benefícios dessa linguagem eficiente.

Entendendo a Arquitetura Hexagonal

A arquitetura hexagonal tem como princípio central a separação de preocupações (SoC - Separation of Concerns). Ela organiza o código em camadas, onde o núcleo da aplicação (lógica de negócios) fica isolado e independente de detalhes de implementação externos, como interfaces de usuário, bancos de dados e frameworks.

Principais Componentes:

  1. Domínio (Core): Contém a lógica de negócios da aplicação. Essa camada é agnóstica quanto à implementação externa e não contém dependências de frameworks ou bibliotecas específicas.
  2. Portas (Ports): Definem interfaces para interações externas. Isso inclui interfaces para serviços, repositórios, notificações, etc. As implementações concretas dessas interfaces são fornecidas por adaptadores.
  3. Adaptadores (Adapters): Implementam as interfaces definidas nas portas. Eles traduzem as chamadas do núcleo da aplicação para o mundo externo, lidando com detalhes de infraestrutura, como acesso a bancos de dados e interações com APIs externas.

Aplicando Hexagonal em Projetos Golang

Adaptando alguns conceitos e para criar a aplicação é possível chegar em um resultado simples e flexível altamente escalável. Vamos usar como exemplo uma aplicação que tem dois usos fundamentais, uma api REST, usada pela aplicação Front-end para comunicação e tráfego dos dados, uma api gRPC para comunicação entre serviços que define modelos de comunicação de uma forma extremamente rápida, leve e independente de linguagem.

REST vs gRPC

REST

  • Utiliza Texto / JSON para se comunicar
  • A comunicação é Unidirecional
  • Alta latência
  • Sem contrato ( maior chance de erros )
  • Sem suporte a streaming
  • Design pré-definido
  • Bibliotecas de terceiros

gRPC

  • Utiliza Protocol Buffers
  • Comunicação Bidirecional e Assíncrona
  • Baixa latência
  • Contrato de comunicação bem definido (.proto)
  • Suporte a Streaming

- Design é livre

- Geração de código

Estrutura de Diretórios:

Ao estruturar um projeto em Go com a arquitetura hexagonal, é comum organizar os diretórios da seguinte maneira:

├───application
│   ├───dtos
│   ├───grpc
│   └───api
│   
├───cmd
|   ├─── rest
|   ├─── grpc
|   └─── main
├───domain
|   |───usecase
|   |───services
│   └───model
└───adapters
  ├───db
  └───repository
  1. cmd: Contém o ponto de entrada das aplicações.
  2. Application: Responsável por iniciar a aplicação e conectar os diferentes componentes, como os servidores ou Microsserviços.
  3. domain: Contém a lógica de negócios da aplicação.
  4. ports: Define as interfaces (portas) para interações externas.
  5. adaprters: Define os adaptadores, módulos de infra-estrutura como bancos de dados e repositórios.

Exemplo de Implementação:

Domínio (domain/service):

package domain type ProductService interface {
  GetProductByID(id int) (*Product, error) 
}

type Product struct { 
  ID int Name string Price float64 
}

repository:

package ports 
import "your_project/internal/domain" 
type ProductRepository interface { 
  GetByID(id int) (*domain.Product, error) 
}

Adaptador (Banco de dados):

package database 

import ( 
  "your_project/internal/domain" 
  "your_project/internal/ports" 
) 

type ProductRepository struct { 
  // implementa a interface ProductRepository 
} 
func (r *ProductRepository) GetByID(id int) (*domain.Product, error) { 
  // lógica de acesso ao banco de dados para obter o produto pelo ID 
}

Porta (servidor gRPC):

func StartGrpcServer(database *DB, port int) {

  grpcServer := grpc.NewServer()
  reflection.Register(grpcServer)
  pixRepository := repository.PixKeyRepositoryDB{Db: database}
  pixUsecase := usecase.PixUseCase{PixKeyRepository: pixRepository}
  pixGrpcService := NewgRPCService(pixUsecase)

  pb.RegisterPixServiceServer(grpcServer, pixGrpcService)

  addres := fmt.Sprintf("0.0.0.0:%d", port)
  listener, err := net.Listen("tcp", addres)
  if err != nil {
      log.Fatalf("Not start gRPC server:%v", err)
  }

  log.Printf("grpc server start on port:%d", port)
  errors := grpcServer.Serve(listener)

  if errors != nil {
      log.Fatalf("Not start gRPC server:%v", err)
  }

}

CMD ponto de entrada do servidor gRPC:

func main() {
  database := db.ConectDB(os.Getenv("env"))
  grpc.StartGrpcServer(database, grpcPortNumber)
}

CMD ponto de entrada do servidor REST:

func main() {
  database := db.ConectDB(os.Getenv("env"))
  rest.StartRESTServer(database, restPortNumber)
}

Benefícios de Usar Hexagonal em Projetos Go

  1. Testabilidade: A separação de preocupações facilita a criação de testes unitários e de integração, permitindo testar a lógica de negócios independentemente das implementações de infraestrutura.
  2. Flexibilidade: A troca de implementações de infraestrutura (banco de dados, API, etc.) é facilitada, pois as mudanças são isoladas nos adaptadores.
  3. Manutenibilidade: A clareza na estrutura do código facilita a manutenção e evolução do software ao longo do tempo.
  4. Desacoplamento: O núcleo da aplicação não depende de detalhes de implementação, tornando-o mais independente e fácil de entender.

Ao aplicar a arquitetura hexagonal em projetos Go, os desenvolvedores podem criar sistemas mais modulares, testáveis e flexíveis, tirando proveito das características únicas da linguagem para construir software robusto e escalável.

Um ótimo exemplo de escalabilidade seria utilizarmos a aplicação para integrar um microserviço utilizando serviços de fila de mensagens como RabbitMQ, Kafka entre outros. Para integrar o serviço não seria necessário modificar o núcleo da aplicação, e sim somente criar um novo ponto de entrada, assim como o gRPC e o REST, e um novo comando para a aplicação, e o contrário também é verdadeiro, é possível modificar toda a regra de negócios da aplicação sem modificar as APIs. Isso mostra como é simples criar aplicações altamente escaláveis e simples adaptando alguns conceitos e trazendo para o contexto da aplicação.

Compartilhe
Comentários (1)
Lucas Martins
Lucas Martins - 21/07/2024 21:19

muito bom, continue assim!