Go

O que Todo Dev Deve Saber sobre Pacotes e Módulos em Go: go mod, imports e Organização de Projetos

11 min de leitura

O que Todo Dev Deve Saber sobre Pacotes e Módulos em Go: go mod, imports e Organização de Projetos

Entendendo Módulos em Go Um módulo em Go é uma coleção de pacotes Go armazenados em um diretório com um arquivo na raiz. Este arquivo define o nome do módulo, a versão mínima do Go necessária e todas as dependências externas do projeto. Antes do Go 1.11, a comunidade dependia do para gerenciar dependências, um modelo que gerava conflitos de versão e dificultava a manutenção. O sistema de módulos resolveu esse problema de forma elegante e robusta. Quando você cria um novo projeto Go, o primeiro passo é inicializar um módulo. Isso é feito com o comando , que cria o arquivo . Este arquivo funciona como um manifesto do seu projeto, similar ao em Node.js ou em Python. O módulo precisa de um nome único, geralmente seguindo a convenção de um caminho de importação (como ). Após executar este comando, você terá um arquivo semelhante a: Este arquivo é fundamental porque documenta exatamente qual versão de cada dependência seu

<h2>Entendendo Módulos em Go</h2>

<p>Um módulo em Go é uma coleção de pacotes Go armazenados em um diretório com um arquivo <code>go.mod</code> na raiz. Este arquivo define o nome do módulo, a versão mínima do Go necessária e todas as dependências externas do projeto. Antes do Go 1.11, a comunidade dependia do <code>GOPATH</code> para gerenciar dependências, um modelo que gerava conflitos de versão e dificultava a manutenção. O sistema de módulos resolveu esse problema de forma elegante e robusta.</p>

<p>Quando você cria um novo projeto Go, o primeiro passo é inicializar um módulo. Isso é feito com o comando <code>go mod init</code>, que cria o arquivo <code>go.mod</code>. Este arquivo funciona como um manifesto do seu projeto, similar ao <code>package.json</code> em Node.js ou <code>requirements.txt</code> em Python. O módulo precisa de um nome único, geralmente seguindo a convenção de um caminho de importação (como <code>github.com/seuusuario/seurepositorio</code>).</p>

<pre><code class="language-bash">go mod init github.com/exemplo/meuprojeto</code></pre>

<p>Após executar este comando, você terá um arquivo <code>go.mod</code> semelhante a:</p>

<pre><code class="language-go">module github.com/exemplo/meuprojeto

go 1.21

require (

github.com/google/uuid v1.3.0

)</code></pre>

<p>Este arquivo é fundamental porque documenta exatamente qual versão de cada dependência seu projeto utiliza. O Go também cria um arquivo <code>go.sum</code>, que contém os hashes criptográficos de todas as dependências — isso garante que qualquer outro desenvolvedor ou máquina de CI/CD sempre baixe as mesmas versões verificadas.</p>

<h2>Sistema de Imports e Pacotes</h2>

<p>Em Go, um pacote é a unidade básica de organização de código. Todo arquivo <code>.go</code> pertence a um pacote, declarado na primeira linha não-comentário com <code>package nomedopacote</code>. Pacotes são diretórios, e todos os arquivos em um diretório devem pertencer ao mesmo pacote. Isso é diferente de muitas linguagens onde você pode ter múltiplos módulos em um arquivo.</p>

<p>Quando você quer usar código de outro pacote, você o importa. O sistema de importação em Go é direto: você especifica o caminho do módulo seguido do caminho do pacote. Se você tem um módulo chamado <code>github.com/exemplo/meuprojeto</code> e dentro dele um pacote <code>utils</code>, qualquer outro projeto importaria esse pacote como:</p>

<pre><code class="language-go">import &quot;github.com/exemplo/meuprojeto/utils&quot;</code></pre>

<p>A partir daí, você acessa as funções, tipos e constantes do pacote <code>utils</code> usando a notação ponto. É importante notar que em Go, apenas identificadores que começam com letra maiúscula são exportados (públicos). Isso é uma convenção aplicada rigidamente pelo compilador — não há palavra-chave <code>public</code> ou <code>private</code>.</p>

<pre><code class="language-go">// arquivo: main.go

package main

import (

&quot;fmt&quot;

&quot;github.com/exemplo/meuprojeto/utils&quot;

)

func main() {

resultado := utils.SomarNumeros(5, 3) // SomarNumeros é exportada (maiúscula)

fmt.Println(resultado)

}</code></pre>

<pre><code class="language-go">// arquivo: utils/math.go

package utils

// Exportada (visível para outros pacotes)

func SomarNumeros(a, b int) int {

return a + b

}

// Não exportada (visível apenas dentro do pacote utils)

func validarNumeros(a, b int) bool {

return a &gt; 0 &amp;&amp; b &gt; 0

}</code></pre>

<p>Você também pode importar apenas para efeitos colaterais usando <code>_</code>, ou dar um alias a um pacote para evitar conflitos de nome:</p>

<pre><code class="language-go">import (

&quot;database/sql&quot;

_ &quot;github.com/lib/pq&quot; // executa init() mas não usa funções

m &quot;math&quot; // alias para evitar conflito

)</code></pre>

<h2>Organização Prática de Projetos</h2>

<p>A organização de diretórios em um projeto Go segue padrões bem estabelecidos na comunidade. A estrutura típica de um projeto profissional tem diretórios com propósitos específicos: <code>cmd/</code> para aplicações executáveis, <code>pkg/</code> para código reutilizável, <code>internal/</code> para código que não deve ser importado por outros projetos, e <code>test/</code> ou <code>testdata/</code> para testes de integração e dados de teste.</p>

<pre><code>meuapp/

├── go.mod

├── go.sum

├── cmd/

│ ├── meuapp/

│ │ └── main.go

│ └── ferramenta/

│ └── main.go

├── pkg/

│ ├── database/

│ │ ├── db.go

│ │ └── migration.go

│ ├── api/

│ │ ├── handler.go

│ │ └── router.go

│ └── utils/

│ └── validate.go

├── internal/

│ ├── config/

│ │ └── config.go

│ └── service/

│ └── business.go

├── test/

│ └── integration_test.go

└── README.md</code></pre>

<p>O diretório <code>cmd/</code> contém o ponto de entrada das aplicações. Se você constrói múltiplas ferramentas a partir do mesmo código base, cada uma tem seu próprio subdiretório em <code>cmd/</code>. O diretório <code>pkg/</code> contém código que você deseja que outros projetos importem — é seguro desde que você mantenha compatibilidade. Já <code>internal/</code> é especial: Go proíbe que outros módulos importem pacotes dentro de <code>internal/</code>, garantindo que código privado de fato seja privado. Isso é verificado pelo próprio compilador durante <code>go build</code> ou <code>go mod tidy</code>.</p>

<p>Exemplo prático de como importar esses pacotes:</p>

<pre><code class="language-go">// cmd/meuapp/main.go

package main

import (

&quot;fmt&quot;

&quot;meuapp/pkg/database&quot; // importa da própria aplicação

&quot;meuapp/pkg/api&quot;

)

func main() {

db := database.Connect(&quot;postgresql://...&quot;)

server := api.NewRouter(db)

server.Start(&quot;:8080&quot;)

}</code></pre>

<pre><code class="language-go">// pkg/api/router.go

package api

import (

&quot;meuapp/internal/config&quot; // OK, mesma aplicação

&quot;meuapp/pkg/database&quot;

)

type Router struct {

db *database.Connection

}

func NewRouter(db database.Connection) Router {

return &amp;Router{db: db}

}</code></pre>

<pre><code class="language-go">// internal/config/config.go

package config

func LoadConfig() map[string]string {

return map[string]string{

&quot;database_url&quot;: &quot;postgresql://...&quot;,

}

}</code></pre>

<p>Se outro projeto tentasse <code>import &quot;meuapp/internal/config&quot;</code>, Go recusaria com um erro de compilação. Isso protege a arquitetura do seu projeto.</p>

<h2>Gerenciando Dependências com go mod</h2>

<p>O comando <code>go mod tidy</code> é seu melhor amigo. Ele remove dependências não utilizadas e adiciona dependências que faltam. Sempre execute-o antes de fazer commit do seu código — garante que <code>go.mod</code> e <code>go.sum</code> estejam sempre sincronizados com o código.</p>

<pre><code class="language-bash">go mod tidy</code></pre>

<p>Para adicionar uma dependência explicitamente, você usa <code>go get</code>. Este comando baixa a versão especificada e atualiza os arquivos <code>go.mod</code> e <code>go.sum</code>:</p>

<pre><code class="language-bash">go get github.com/gorilla/mux@v1.8.0

go get github.com/lib/pq@latest

go get -u ./... # atualiza todas as dependências diretas</code></pre>

<p>Às vezes você quer entender quais dependências seu projeto tem e por quê. O comando <code>go mod graph</code> mostra o grafo de dependências:</p>

<pre><code class="language-bash">go mod graph</code></pre>

<p>A saída mostra as relações: <code>meuapp github.com/gorilla/mux@v1.8.0</code> significa que seu módulo depende diretamente de <code>mux</code> na versão 1.8.0.</p>

<p>Para verificar se há vulnerabilidades conhecidas em suas dependências:</p>

<pre><code class="language-bash">go list -json -m all | nancy sleuth</code></pre>

<p>(Usando o tool <code>nancy</code>, que você instala com <code>go install github.com/sonatype-nexus-community/nancy@latest</code>)</p>

<p>Um arquivo <code>go.mod</code> bem mantido fica assim:</p>

<pre><code class="language-go">module github.com/exemplo/meuservidor

go 1.21

require (

github.com/gorilla/mux v1.8.0

github.com/lib/pq v1.10.7

)

require (

github.com/stretchr/testify v1.8.0 // indirect

)

retract v0.0.1 // bug crítico encontrado</code></pre>

<p>A seção <code>require</code> lista dependências diretas que seu código importa. A seção <code>indirect</code> mostra dependências que são necessárias por suas dependências diretas — Go as inclui automaticamente quando você executa <code>go mod tidy</code>. A tag <code>retract</code> permite depreciar versões inteiras, útil quando você descobre um bug crítico após publicar.</p>

<p>Quando você trabalha com versões, Go segue versionamento semântico: <code>v1.2.3</code> significa major.minor.patch. Uma dependência pode ser atualizada para patch (<code>1.2.4</code>) e minor (<code>1.3.0</code>) sem quebra esperada, mas major (<code>2.0.0</code>) pode ter breaking changes — você precisa atualizar seu código para usar a nova API.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://golang.org/doc/modules" target="_blank" rel="noopener noreferrer">Go Modules - Official Documentation</a></li>

<li><a href="https://golang.org/doc/effective_go#package-names" target="_blank" rel="noopener noreferrer">Effective Go - Package management</a></li>

<li><a href="https://blog.golang.org/using-go-modules" target="_blank" rel="noopener noreferrer">Using Go Modules - Go Blog</a></li>

<li><a href="https://github.com/go-modules-by-example" target="_blank" rel="noopener noreferrer">Go Modules by Example</a></li>

<li><a href="https://golang.org/ref/spec#Packages" target="_blank" rel="noopener noreferrer">The Go Programming Language Specification</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Go

Boas Práticas de sync.WaitGroup e sync.Once em Go: Coordenação de Goroutines para Times Ágeis
Boas Práticas de sync.WaitGroup e sync.Once em Go: Coordenação de Goroutines para Times Ágeis

Entendendo Goroutines e a Necessidade de Sincronização Go foi projetado com c...

Stack vs Heap em Go: Escape Analysis e Alocação Eficiente: Do Básico ao Avançado
Stack vs Heap em Go: Escape Analysis e Alocação Eficiente: Do Básico ao Avançado

Fundamentos de Stack e Heap em Go A memória em qualquer programa está organiz...

Dominando Domain-Driven Design em Go: Entidades, Value Objects e Repositórios em Projetos Reais
Dominando Domain-Driven Design em Go: Entidades, Value Objects e Repositórios em Projetos Reais

Domain-Driven Design: Fundamentos e Aplicação em Go Domain-Driven Design (DDD...