Socorro! Como posso organizar os pacotes em Go?
Tags: go, beginners
Published: 2020-02-23 20:34:56

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](https://github.com/segmentio/kafka-go) da [Segment](https://segment.com/) utiliza uma estrutura completamente diferente da biblioteca [confluent-kafka-go](https://github.com/confluentinc/confluent-kafka-go) da [Confluent](https://www.confluent.io/).

## 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](https://golang.org/doc/code.html#Organization), 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](https://dev-to-uploads.s3.amazonaws.com/i/ntmb2wp60lbty5x9gr3t.png)

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](https://dev-to-uploads.s3.amazonaws.com/i/otujj8qfgagb19uo2muu.png)

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](https://dev-to-uploads.s3.amazonaws.com/i/nuzhuszv91x7if5ncuzl.png)

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](https://rakyll.org/style-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](https://dev-to-uploads.s3.amazonaws.com/i/cn33e1w81pgwhx5qte2r.png)

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](https://blog.golang.org/package-names) feita pelo [Sameer Ajmani](https://twitter.com/sajma), 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](https://github.com/kubernetes/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](https://twitter.com/rakyll) 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](https://rakyll.org/style-packages/#no-plurals).

## 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](https://github.com/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](https://github.com/golang-standards/project-layout/blob/master/pkg/README.md) 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](https://github.com/hashicorp/terraform/tree/master/scripts) 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](https://github.com/golang-standards/project-layout#src) 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ń](https://twitter.com/kasiazien) 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](https://youtu.be/VQym87o91f8) 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](https://invite.slack.golangbridge.org/) 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](https://www.pexels.com/pt-br/@apasaric) no [Pexels](https://www.pexels.com/pt-br/foto/amontoado-cartao-cartolina-castanho-1555199/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels).