Como implementar uma API com arquitetura hexagonal

Hoje iniciamos mais uma pequena série aqui no blog, onde vamos implementar uma pequena API utilizando arquitetura hexagonal. Essa série será dividida em pelo menos quatro posts, sendo esse primeiro a definição da estrutura de pastas.

Se você ainda não conhece muito bem essa arquitetura, convido você a ler o post onde explico a parte teórica da arquitetura hexagonal.

Agora que estamos todos no mesmo ponto, vamos começar a definir a estrutura de pastas desse pequeno projeto.

pkg

No root do projeto, como já é de costume, precisamos de uma pasta onde ficarão as implementações que podem ser compartilhadas com outros projetos, ou seja, implementações de packages que não tem nenhuma regra de negócio.

Por padrão, chamamos essa pasta de pkg. Dentro dela, serão criados os packages para conexão com o banco de dados, conexão com filas, logs, agentes de observabilidade e etc.

Como minha idéia para esse projeto é utilizar Mongodb como banco de dados, dentro da pasta pkg, precisamos criar a pasta mongo, onde implementaremos a conexão o banco.

internal

Na linguagem Go, para evitar que algum package seja importado por outros projetos, precisamos implementá-los dentro da pasta internal. Esse é o único nome de pasta que tem um comportamento diferente na linguagem.

Sendo assim, dentro dessa pasta vamos criar todos os packages que implementem as camadas core, port e adapter da arquitetura hexagonal.

category

Como vamos implementar uma simples API com CRUD de categorias, dentro da pasta internal, teremos uma pasta category com dois arquivos, entity.go e ports.go. Nesses arquivos vamos definir a entidade e interfaces do package, respectivamente.

Ainda dentro da pasta category, precisamos criar mais duas pastas. A primeira, que se chamará handlers, e será utilizada para implementar os adapters do tipo driver. Já a segunda, que se chamará repository, será utilizada para implementar os adapters do tipo driven.

cmd

Nessa pasta, que também é criada no root do projeto, é onde ficam os packages com implementações de entrada dos programas, ou seja, o package e função main.

Repare que eu disse programas ao invés de programa. Isso por que, em implementações Go, é muito comum um único repositório conter mais de um programa. Além de facilitar a manutenção, conseguimos reutilizar código de uma forma muito mais simples.

Sendo assim, como nessa série vamos implementar uma API, dentro da pasta cmd, criamos uma pasta api com um arquivo main.go dentro.

Conclusão

Ao fim desse primeiro post, ficamos com a seguinte estrutura de pastas e arquivos definida:

root
|
|-- cmd
|   |-- api
|       |-- main.go
|-- internal
|   |-- category
|       |-- handlers
|       |-- repository
|       |-- entity.go
|       |-- ports.go
|-- pkg
|   |-- mongo
|-- go.mod
|-- go.sum

A meu ver, essa separação deixa muito claro onde está cada componente e camada da aplicação.

Se você ainda não é inscrito na nossa newsletter, não deixe de se inscrever para não perder os próximos posts, onde vamos implementar o package mongo. Até a próxima!


Se inscreva na nossa newsletter

* indicates required

8 comentários sobre “Como implementar uma API com arquitetura hexagonal

  1. como ficaria a questão dos package com muitos modulos? Exe: user, post… Não teria muito package com o nome igual não?

    • Teria os packages `handlers` e `repository` para cada um desses packages que você exemplificou.
      No entanto, ao fazer o import deles podemos usar um alias, como por exemplo `uh` para o handler do user e `ph` para o handler de post.

  2. Tiago, gostei da sua proposta de organização de pastas. Uma dúvida: se eu tiver outra entidade, por exemplo “pessoa”, eu não poderia ter os pacotes “handlers” e “repository” novamente, pois eles entrariam em conflito com os da entidade “category”. Como você resolveria esse problema?

  3. Organizei os pacotes como você mencionou, Tiago. Está ficando legal. Outra dúvida aqui: onde você colocaria os serviços (services) ou casos de uso? Dentro do pacote de cada entidade (categoria, pessoa, etc)?

Deixe uma resposta