Como organizar um projeto Go com vários arquivos: por baixo dos panos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Projeto Go de um arquivo só é fácil. E de dez?

\n\n

Imagine: você está escrevendo em Go um utilitário para análise de logs. No começo, tudo cabe em um único arquivo main.go. Funciona, tudo ótimo. Mas depois de uma semana, o código cresce: surgem funções para ler arquivos, para filtrar, para enviar dados para o banco de dados. Um arquivo se transforma em um monstro de 2000 linhas. Familiar?

\n\n

Aqui surge a pergunta que atormenta um em cada dois iniciantes (e que rendeu milhares de visualizações no Stack Overflow): como organizar corretamente um projeto Go com vários arquivos? Não apenas espalhar o código pelas pastas, mas fazer com que funcione, compile e não gere erros como undefined ou import cycle not allowed.

\n\n

Neste artigo, não vamos apenas dar um modelo. Vamos olhar por baixo dos panos do compilador Go e entender como realmente funciona a ligação entre arquivos, pacotes e módulos. Você descobrirá por que go run *.go às vezes salva e às vezes não, e como construir uma arquitetura que escala.

\n\n

Arquivo, pacote, módulo — a hierarquia básica

\n\n

Antes de mergulhar no código, vamos analisar três conceitos-chave. Sem eles, a organização de um projeto Go se torna um jogo de adivinhação.

\n\n
    \n
  • Arquivo (.go) — é apenas um arquivo de texto com código. Por si só, não significa nada para o compilador.
  • \n
  • Pacote (package) — é um grupo lógico de arquivos que estão no mesmo diretório. Todos os arquivos em uma pasta devem ter a mesma declaração package. Se na pasta houver arquivos com pacotes diferentes, o compilador emitirá um erro.
  • \n
  • Módulo (go.mod) — é a raiz do seu projeto. Ele define o nome do módulo, suas dependências e versões do Go. Um módulo pode conter vários pacotes.
  • \n
\n\n

Regra de ouro: um diretório = um pacote. A exceção são os testes, mas falaremos sobre eles depois.

\n\n

Exemplo prático: projeto "Analisador de Logs"

\n\n

Vamos criar um pequeno projeto que lê um arquivo de log, filtra as linhas por uma palavra-chave e exibe o resultado. Faremos isso primeiro "de forma direta" (um arquivo) e depois dividiremos corretamente em vários arquivos.

\n\n

Etapa 1: Tudo em um único arquivo (ruim)

\n\n
// main.go\npackage main\n\nimport (\n    "bufio"\n    "fmt"\n    "os"\n    "strings"\n)\n\nfunc readLog(path string) ([]string, error) {\n    file, err := os.Open(path)\n    if err != nil {\n        return nil, err\n    }\n    defer file.Close()\n\n    var lines []string\n    scanner := bufio.NewScanner(file)\n    for scanner.Scan() {\n        lines = append(lines, scanner.Text())\n    }\n    return lines, scanner.Err()\n}\n\nfunc filterLines(lines []string, keyword string) []string {\n    var result []string\n    for _, line := range lines {\n        if strings.Contains(line, keyword) {\n            result = append(result, line)\n        }\n    }\n    return result\n}\n\nfunc main() {\n    lines, err := readLog("server.log")\n    if err != nil {\n        fmt.Println("Erro:", err)\n        return\n    }\n    filtered := filterLines(lines, "ERROR")\n    for _, l := range filtered {\n        fmt.Println(l)\n    }\n}\n
\n\n

Funciona. Mas imagine que readLog se torne mais complexo (precisa de suporte a gzip, codificações) e filterLines fique cheio de expressões regulares. O arquivo se tornará ilegível.

\n\n

Etapa 2: Dividindo em pacotes (correto)

\n\n

Vamos criar a estrutura de diretórios:

\n\n
log-analyzer/\n├── go.mod\n├── main.go\n└── pkg/\n    └── logreader/\n        ├── reader.go\n        └── filter.go\n
\n\n

Inicializamos o módulo:

\n\n
go mod init log-analyzer
\n\n

Agora vamos escrever o código do pacote logreader. Observe: todos os arquivos na pasta pkg/logreader/ têm package logreader.

\n\n

pkg/logreader/reader.go

\n\n
package logreader                

Blogs

Book Recommendations