DEV Community

Cover image for Socorro! Como posso organizar os pacotes em Go?
Odilon Jonathan Kröger
Odilon Jonathan Kröger

Posted on

Socorro! Como posso organizar os pacotes em Go?

Hoje vou falar a respeito de algo que tive certa dificuldade quando comecei a mexer com Go, que é a organização de pacotes.
Para falar a verdade, esse assunto não tem uma resposta rígida. Por quê? Simples, se você navegar pelo GitHub, encontrará projetos Go com diferentes tipos de estruturação. Por exemplo, a biblioteca kafka-go da Segment utiliza uma estrutura completamente diferente da biblioteca confluent-kafka-go da Confluent.

O que a documentação oficial tem a dizer sobre pacotes?

Programas Go são organizados em pacotes. Um pacote é uma coleção de arquivos fonte dentro de um mesmo diretório e que são compilados em conjunto. Funções, tipos, variáveis e constantes definidos em um arquivo fonte são visíveis para todos outros arquivos fonte dentro de um mesmo pacote. (Tradução minha).

Vou resumir aqui um pouco a documentação oficial, sem entrar nos detalhes do uso de módulos. O código é bastante similar ao da documentação oficial, com pequenas alterações e alguma tradução.

Base

Como podemos ver, existe o diretório raiz chamado estrutura-de-pacotes com dois arquivos dentro, ola_mundo.go e ola_mundo_test.go.
Até aqui nenhum mistério.
Se observar na imagem, estamos no package main.

Mas considerando que eu vou criar um pacote novo, chamado strutil, todo arquivo que existir dentro dele, é visível para ele mesmo, mas não para o pacote raíz. Em outras palavras, para utilizar o código que está dentro do arquivo strutil.go em outros pacotes, no meu caso o pacote raíz, é necessário importar o pacote no arquivo ola_mundo.go e ola_mundo_test.go, conforme a imagem abaixo.

Pacote main e strutil

Ah, um detalhe! Para que a função consiga ser exportada para outros pacotes como no caso acima, o nome da função precisa iniciar com letra maiúscula. Se eu tivesse utilizado o nome reverter() em vez de Reverter(), não seria possível utilizar a função dentro do arquivo ola_mundo.go.

Arquivo strutil.go

No mundo Go é uma boa prática comentar as funções que podem ser exportadas, pois gerando o godoc, o comentário aparece na documentação.
Algumas pessoas podem dizer que um bom código não precisa de comentários, mas quando o código será exposto por uma documentação godoc para alguém fazer uso da nossa API por exemplo, simples comentários podem ajudar muito nosso cliente.
O artigo Style guideline for Go packages inclusive fala que é um bom exercício ficar de olho no godoc no início do projeto, para ler e validar se realmente faz sentido a separação de pacotes que está sendo utilizada.

godoc

Com a leitura acima, podemos entender que um pacote fica contido dentro de um diretório e caso eu necessite utilizar em outros pacotes, preciso importar o pacote com o código desejado.

Como eu devo nomear os pacotes?

Existe uma publicação feita pelo Sameer Ajmani, com detalhes sobre essa questão. Mas existem alguns pontos básicos que vou explicar aqui.

  • O nome de um pacote deve ser curto e fácil de entender, mas sem comprometer a sua "intenção". Por exemplo, se eu quero escrever um pacote com utilidades relacionadas ao banco de dados, meu pacote poderia se chamar dbutil. Alguém pode pensar em chamar de util, mas como o nome é genérico demais, acaba prejudicando o entendimento do que tudo pode existir dentro do pacote.
  • O uso de sublinhado _, hífen - ou letras maiúsculas é desencorajado. Por exemplo, posso chamar um pacote de validacaologin, mas eu não deveria chama-lo de validacaoLogin, validacao-login ou ainda validacao_login. Porém, como eu disse no início da publicação, diferentes empresas podem seguir seus próprios padrões, como pode ser observado no repositório do kubernetes. Eu percebi que é difícil encontrar repositórios com pacotes cujo nome contém mais de uma palavra. Acredito ser boa abordagem se esforçar para utilizar apenas uma palavra como nome de pacote.
  • Abreviação é permitida, mas eu particularmente prefiro utiliza-la com cuidado. Exemplos de pacotes com nome abreviado: cmd, fmt, bufio.
  • Pacotes dentro de diretórios diferentes podem ter o mesmo nome. Por exemplo, posso ter cobranca/lancamento e também investimento/lancamento. Mas atenção! Se esses pacotes forem importados com frequência no mesmo lugar, o ideal é que um deles seja renomeado se possível, evitando problemas de entendimento.
  • Os diretórios dos pacotes podem ser criados da melhor forma que você encontrar para organizar o código. Você pode criar um diretório onde colocará um determinado domínio, ou ainda um diretório onde ficará concentrado todo código relacionado à algum tipo de computação específica.
  • A Jaana Dogan-Dalgaard chama atenção para algo que nós que viemos de outras linguagens acabamos estranhando. O nome do pacote deveria ser no singular e não no plural.

Afinal, existe algum padrão adotado?

Como mencionei acima, é possível encontrar diversos tipos de estruturas pelo GitHub. Mas existe um repositório com a sugestão de um padrão bem genérico que você pode ter como base, chamado golang-standards/project-layout.
Esse layout não é oficial e dependendo do tamanho do seu projeto, utiliza-lo seria complicar demais as coisas. Comece sempre da forma mais simples possível e vá adicionando novos pacotes conforme a necessidade for surgindo.

Alguns pacotes que são encontrados com frequência em projetos Go e que são explicados no README deste repositório. De forma resumida:

  • cmd é o pacote onde você concetra os seus arquivos main.go.
  • internal é o pacote onde fica o código que você não quer expor para fora da aplicação ou biblioteca. Conforme vimos acima, é possível ter vários pacotes internal dentro de diferentes diretórios.
  • pkg é o pacote onde fica o código que pretendo expor para ser utilizado em outras aplicações. O código exposto aqui será utilizado por outras pessoas, portanto escolha com cuidado o código que ficará dentro deste pacote. Você pode encontrar aqui uma lista de repositórios que usa este padrão.
  • vendor é o pacote onde você pode adicionar suas dependências.
  • api é onde você pode colocar schemas de JSON, Swagger, etc.
  • configs é onde ficam arquivos comuns de configuração. Observe que o nome é no plural!
  • scripts é utilizado para concentrar os scripts utilizados pela aplicação. Existe um ótimo exemplo no repositório do Terraform.
  • deployments é utilizado para os arquivos docker-compose, de Terraform, etc.

Atenção para quem vem do Java como eu vim! É muito comum que a gente pense logo em criar um pacote chamado src, porém essa prática é desencorajada, conforme a documentação do layout que citei acima.

You really don't want your Go code or Go projects to look like Java :-)

Como posso amadurecer a estrutura do meu projeto? Posso usar DDD?

Existe uma palestra, da Kat Zień que mostra como estruturar um projeto, utilizado apenas um diretório e ir evoluindo conforme a necessidade até atingir uma estrutura baseada em DDD.
Vou deixar aqui para a palestra.

Concluindo...

No fim das contas, como Go é uma linguagem nova e bastante versátil, por mais que existam alguns padrões surgindo, dependendo do projeto faz mais sentido seguir um ou outro caminho. Pra mim essa é uma característica que amo em Go, a flexibilidade.
Quando for estruturar o projeto, siga as boas práticas de comentar pacotes e funções que serão expostas, validando no godoc se faz sentido. Converse com sua equipe e com pessoas de outros lugares, como no Slack do Gophers para entender como outros projetos estão sendo feitos e quais prós e contras foram encontrados.
Considere também aquilo que fica mais confortável para sua equipe. De nada adianta criar uma guerra interna por causa da nomenclatura de pacotes e não ter o projeto entregue!

Por favor, não esqueça de cobrir o seu código com testes. ; )


Todos artigos que utilizei para estudar e criar esta publicação foram citados acima com seus respectivos links.

Foto de de capa feita por Aleksandar Pasaric no Pexels.

Top comments (1)

Collapse
 
grachetp profile image
Pedro Grachet

Gostei do artigo!