Pacotes e Modulos

Licao 11: Pacotes e Modulos

Analogia do Mundo Real: Imagine uma grande biblioteca. Cada livro e colocado em prateleiras diferentes por categoria — Literatura, Ciencia, Historia... Isso e um "pacote." O sistema de indice da biblioteca diz onde cada prateleira esta — isso e um "modulo." Voce nao precisa empilhar todos os livros em uma sala; categoriza-los e como voce encontra eficientemente o que precisa. O sistema de pacotes do Go e a classificacao de bibliotecas do mundo do codigo.


Conceitos Principais

Conceito Descricao
Pacote A unidade organizacional do codigo fonte Go; todos os arquivos .go em um diretorio pertencem ao mesmo pacote
Modulo Uma colecao de pacotes relacionados, definido pelo arquivo go.mod; a menor unidade de gerenciamento de dependencias
Importacao Usar identificadores exportados de outros pacotes
Regras de Exportacao Identificadores comecando com letra maiuscula sao acessiveis de pacotes externos; minuscula significa privado do pacote
Pacote interno Nome de diretorio especial; pacotes internos so podem ser importados por codigo em sua arvore de diretorios pai
go get Baixar e instalar pacotes de terceiros de repositorios remotos

Relacao Entre Pacotes e Modulos

meu-modulo/              ← Raiz do modulo, contem go.mod
├── go.mod
├── main.go             ← package main
├── mathutil/           ← Sub-pacote mathutil
│   └── calc.go         ← package mathutil
└── internal/           ← Pacote interno
    └── secret.go       ← package internal

Sintaxe e Uso Basico

1. Declaracao de package

Todo arquivo fonte Go deve ter como primeira linha uma declaracao package:

GO
package main // Pacote de entrada para programas executaveis

package mathutil // Pacote utilitario
💡 Dica: Todos os arquivos .go no mesmo diretorio devem declarar o mesmo nome de pacote (geralmente correspondendo ao nome do diretorio).

2. Inicializando um Modulo

BASH
# Executar no diretorio raiz do projeto
go mod init meu-modulo

# Gera o arquivo go.mod:
# module meu-modulo
#
# go 1.24
💡 Dica: Caminhos de modulo geralmente sao enderecos de repositorio, como github.com/usuario/meu-modulo, facilitando a referencia por outros.

3. Limpando Dependencias

BASH
# Adicionar dependencias faltantes, remover nao utilizadas
go mod tidy
💡 Dica: Execute go mod tidy toda vez que introduzir um novo pacote de terceiros.

4. Declaracoes import

GO
import "fmt"                    // Biblioteca padrao
import "github.com/gin-gonic/gin" // Pacote de terceiros

// Importacoes agrupadas (recomendado)
import (
    "fmt"
    "log"
    "os"
)

5. Regras de Exportacao

GO
package mathutil

var Pi = 3.14159      // Comeca com maiuscula → exportado, visivel externamente
var versao = "1.0"    // Comeca com minuscula → nao exportado, privado do pacote

func Somar(a, b int) int { // Maiuscula → exportado
    return a + b
}

func subtrair(a, b int) int { // Minuscula → nao exportado
    return a - b
}
💡 Dica: As regras de exportacao se aplicam a variaveis, constantes, tipos, funcoes, campos de struct, metodos — todos os identificadores seguem esta regra.

6. Pacote interno

meuapp/
├── internal/
│   └── auth/        ← Apenas meuapp/ e seus subdiretorios podem importar
│       └── auth.go
└── cmd/
    └── server/
        └── main.go ← ✅ Pode importar meuapp/internal/auth
💡 Dica: internal e controle de acesso forcado pelo compilador, nao uma convencao — qualquer pacote fora de sua arvore de diretorios pai nao pode importa-lo.

7. Instalando Pacotes de Terceiros

BASH
# Baixar pacote e atualizar go.mod / go.sum
go get github.com/gin-gonic/gin

# Especificar versao
go get github.com/gin-gonic/gin@v1.9.1

# Remover dependencias nao utilizadas
go mod tidy

Exemplos

Exemplo: Criando e Usando Pacotes Personalizados (Dificuldade ⭐)

Estrutura do projeto:

app-saudacao/
├── go.mod
├── main.go
└── saudacao/
    └── saudacao.go
▶ Experimente

saudacao/saudacao.go:

GO
package saudacao

import "fmt"

// Ola retorna uma saudacao (maiuscula, exportado)
func Ola(nome string) string {
    return fmt.Sprintf("Ola, %s! Bem-vindo a programacao Go.", nome)
}

// adeus retorna uma mensagem de despedida (minuscula, nao exportado)
func adeus(nome string) string {
    return fmt.Sprintf("Tchau, %s!", nome)
}

// Adeus e um wrapper exportado para adeus (maiuscula, exportado)
func Adeus(nome string) string {
    return adeus(nome)
}

main.go:

GO
package main

import (
    "fmt"
    "app-saudacao/saudacao" // Importar sub-pacote local
)

func main() {
    // Usar funcoes exportadas
    fmt.Println(saudacao.Ola("Alice"))
    fmt.Println(saudacao.Adeus("Alice"))

    // ❌ O codigo abaixo causara erro: adeus nao e exportado
    // fmt.Println(saudacao.adeus("Alice"))
}

Executar:

BASH
go run main.go

Saida:

TEXT
Ola, Alice! Bem-vindo a programacao Go.
Tchau, Alice!

Exemplo: Usando Pacotes de Terceiros (Dificuldade ⭐⭐)

Inicializar projeto:

BASH
mkdir http-demo && cd http-demo
go mod init http-demo
go get github.com/gin-gonic/gin
▶ Experimente

main.go:

GO
package main

import (
    "net/http"

    "github.com/gin-gonic/gin" // Framework web de terceiros
)

// Usuario define uma struct de usuario
type Usuario struct {
    Nome  string `json:"nome"`
    Email string `json:"email"`
}

func main() {
    r := gin.Default() // Criar engine padrao

    // Requisicao GET: retornar mensagem de boas-vindas
    r.GET("/", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "mensagem": "Bem-vindo ao tutorial de gerenciamento de pacotes Go!",
        })
    })

    // Requisicao GET: retornar informacoes do usuario
    r.GET("/usuario", func(c *gin.Context) {
        usuario := Usuario{
            Nome:  "Alice",
            Email: "alice@exemplo.com",
        }
        c.JSON(http.StatusOK, usuario)
    })

    // Iniciar servidor, escutar na porta 8080
    r.Run(":8080")
}

Executar:

BASH
go run main.go
# Apos o servidor iniciar, acesse http://localhost:8080

Exemplo: Pacote interno e Colaboracao Multi-Pacote (Dificuldade ⭐⭐⭐)

Estrutura do projeto:

sistema-bancario/
├── go.mod
├── main.go
├── conta/
│   └── conta.go       ← Pacote conta (publico)
├── internal/
│   └── validador/
│       └── validador.go ← Pacote validador (apenas interno)
└── transacao/
    └── transacao.go   ← Pacote transacao (publico)
▶ Experimente

go.mod:

TEXT
module sistema-bancario

go 1.24

internal/validador/validador.go:

GO
package validador

import "errors"

// ValidarValor valida valor de transacao (apenas pacote interno)
func ValidarValor(valor float64) error {
    if valor <= 0 {
        return errors.New("valor deve ser maior que zero")
    }
    if valor > 1000000 {
        return errors.New("transacao unica nao pode exceder 1 milhao")
    }
    return nil
}

// ValidarIDConta valida ID da conta
func ValidarIDConta(id string) error {
    if len(id) < 6 {
        return errors.New("ID da conta deve ter pelo menos 6 caracteres")
    }
    return nil
}

conta/conta.go:

GO
package conta

import (
    "fmt"
    "sistema-bancario/internal/validador" // Importar pacote interno
)

// Conta struct
type Conta struct {
    ID      string
    Nome    string
    Saldo   float64
}

// NovaConta cria uma nova conta
func NovaConta(id, nome string, saldo float64) (*Conta, error) {
    // Usar funcao de validacao do pacote interno
    if err := validador.ValidarIDConta(id); err != nil {
        return nil, fmt.Errorf("falha na criacao da conta: %w", err)
    }
    if saldo < 0 {
        return nil, fmt.Errorf("saldo inicial nao pode ser negativo")
    }
    return &Conta{
        ID:    id,
        Nome:  nome,
        Saldo: saldo,
    }, nil
}

// Depositar deposita dinheiro
func (c *Conta) Depositar(valor float64) error {
    // Usar funcao de validacao do pacote interno
    if err := validador.ValidarValor(valor); err != nil {
        return fmt.Errorf("falha no deposito: %w", err)
    }
    c.Saldo += valor
    return nil
}

// ObterSaldo obtem o saldo
func (c *Conta) ObterSaldo() float64 {
    return c.Saldo
}

transacao/transacao.go:

GO
package transacao

import (
    "fmt"
    "sistema-bancario/conta"
    "sistema-bancario/internal/validador" // Tambem pode importar pacote interno
)

// Transferir funcao de transferencia
func Transferir(de, para *conta.Conta, valor float64) error {
    // Usar pacote interno para validar valor
    if err := validador.ValidarValor(valor); err != nil {
        return fmt.Errorf("falha na transferencia: %w", err)
    }

    if de.ObterSaldo() < valor {
        return fmt.Errorf("falha na transferencia: saldo insuficiente (atual: %.2f)", de.ObterSaldo())
    }

    // Executar transferencia
    if err := de.Depositar(-valor); err != nil {
        return fmt.Errorf("falha na deducao: %w", err)
    }
    if err := para.Depositar(valor); err != nil {
        // Rollback
        de.Depositar(valor)
        return fmt.Errorf("falha no credito: %w", err)
    }

    return nil
}

main.go:

GO
package main

import (
    "fmt"
    "sistema-bancario/conta"
    "sistema-bancario/transacao"
    // ❌ A importacao abaixo falhara: pacote interno nao pode ser importado externamente
    // "sistema-bancario/internal/validador"
)

func main() {
    // Criar duas contas
    conta1, err := conta.NovaConta("ACC001", "Alice", 10000)
    if err != nil {
        fmt.Println("Erro:", err)
        return
    }

    conta2, err := conta.NovaConta("ACC002", "Bob", 5000)
    if err != nil {
        fmt.Println("Erro:", err)
        return
    }

    fmt.Printf("Antes da transferencia - Alice: %.2f, Bob: %.2f\n", conta1.ObterSaldo(), conta2.ObterSaldo())

    // Executar transferencia
    err = transacao.Transferir(conta1, conta2, 3000)
    if err != nil {
        fmt.Println("Erro:", err)
        return
    }

    fmt.Printf("Apos transferencia - Alice: %.2f, Bob: %.2f\n", conta1.ObterSaldo(), conta2.ObterSaldo())

    // Testar valor invalido
    err = conta1.Depositar(-100)
    if err != nil {
        fmt.Println("Erro esperado:", err)
    }
}

Executar:

BASH
go run main.go

Saida:

TEXT
Antes da transferencia - Alice: 10000.00, Bob: 5000.00
Apos transferencia - Alice: 7000.00, Bob: 8000.00
Erro esperado: falha no deposito: valor deve ser maior que zero

Cenarios de Aplicacao do Mundo Real

Cenario 1: Construindo um Pacote de Logger Reutilizavel

meu-app/
├── go.mod
├── main.go
└── logger/
    └── logger.go

logger/logger.go:

GO
package logger

import (
    "fmt"
    "os"
    "time"
)

// Nivel nivel de log
type Nivel int

const (
    NivelDebug Nivel = iota // 0
    NivelInfo               // 1
    NivelWarn               // 2
    NivelError              // 3
)

// Logger struct
type Logger struct {
    prefixo string
    nivel   Nivel
}

// Novo cria uma instancia de logger
func Novo(prefixo string, nivel Nivel) *Logger {
    return &Logger{
        prefixo: prefixo,
        nivel:   nivel,
    }
}

// log metodo interno para saida de logs
func (l *Logger) log(nivel Nivel, tag, msg string) {
    if nivel < l.nivel {
        return
    }
    timestamp := time.Now().Format("2006-01-02 15:04:05")
    linha := fmt.Sprintf("[%s] [%s] [%s] %s\n", timestamp, tag, l.prefixo, msg)
    os.Stdout.WriteString(linha)
}

// Debug log de debug
func (l *Logger) Debug(msg string) {
    l.log(NivelDebug, "DEBUG", msg)
}

// Info log de info
func (l *Logger) Info(msg string) {
    l.log(NivelInfo, "INFO", msg)
}

// Warn log de aviso
func (l *Logger) Warn(msg string) {
    l.log(NivelWarn, "WARN", msg)
}

// Error log de erro
func (l *Logger) Error(msg string) {
    l.log(NivelError, "ERROR", msg)
}

main.go:

GO
package main

import (
    "meu-app/logger"
)

func main() {
    log := logger.Novo("APP", logger.NivelInfo)

    log.Debug("Isso nao vai aparecer porque o nivel esta abaixo de Info")
    log.Info("Aplicacao iniciada")
    log.Warn("Espaco em disco baixo")
    log.Error("Falha na conexao com banco de dados")
}

Saida:

TEXT
[2026-06-26 10:30:00] [INFO] [APP] Aplicacao iniciada
[2026-06-26 10:30:00] [WARN] [APP] Espaco em disco baixo
[2026-06-26 10:30:00] [ERROR] [APP] Falha na conexao com banco de dados

Cenario 2: Organizacao de Pacotes em Arquitetura Camadas

Uma estrutura tipica de projeto de servico web:

servico-web/
├── go.mod
├── go.sum
├── main.go
├── config/
│   └── config.go        ← Gerenciamento de configuracao
├── internal/
│   ├── model/
│   │   └── usuario.go   ← Modelo de dados
│   ├── repositorio/
│   │   └── usuario_repo.go ← Camada de acesso a dados
│   └── servico/
│       └── usuario_servico.go ← Camada de logica de negocio
└── handler/
    └── usuario_handler.go ← Camada de handler HTTP

config/config.go:

GO
package config

// Config configuracao da aplicacao
type Config struct {
    Porta    string
    DBHost   string
    DBPorta  int
    LogNivel string
}

// Carregar carrega configuracao (exemplo simplificado)
func Carregar() *Config {
    return &Config{
        Porta:    "8080",
        DBHost:   "localhost",
        DBPorta:  5432,
        LogNivel: "info",
    }
}

internal/model/usuario.go:

GO
package model

// Usuario modelo
type Usuario struct {
    ID    int    `json:"id"`
    Nome  string `json:"nome"`
    Email string `json:"email"`
}

internal/repositorio/usuario_repo.go:

GO
package repositorio

import (
    "fmt"
    "servico-web/internal/model"
)

// RepositorioUsuario repositorio de dados de usuarios
type RepositorioUsuario struct {
    usuarios map[int]*model.Usuario
    proximoID int
}

// NovoRepositorioUsuario cria uma instancia de repositorio
func NovoRepositorioUsuario() *RepositorioUsuario {
    return &RepositorioUsuario{
        usuarios:  make(map[int]*model.Usuario),
        proximoID: 1,
    }
}

// Criar cria um usuario
func (r *RepositorioUsuario) Criar(nome, email string) *model.Usuario {
    usuario := &model.Usuario{
        ID:    r.proximoID,
        Nome:  nome,
        Email: email,
    }
    r.usuarios[r.proximoID] = usuario
    r.proximoID++
    return usuario
}

// BuscarPorID encontra um usuario por ID
func (r *RepositorioUsuario) BuscarPorID(id int) (*model.Usuario, bool) {
    usuario, ok := r.usuarios[id]
    return usuario, ok
}

// BuscarTodos encontra todos os usuarios
func (r *RepositorioUsuario) BuscarTodos() []*model.Usuario {
    resultado := make([]*model.Usuario, 0, len(r.usuarios))
    for _, u := range r.usuarios {
        resultado = append(resultado, u)
    }
    return resultado
}

// String retorna um resumo do repositorio
func (r *RepositorioUsuario) String() string {
    return fmt.Sprintf("RepositorioUsuario{%d usuarios no total}", len(r.usuarios))
}

main.go:

GO
package main

import (
    "fmt"
    "servico-web/config"
    "servico-web/internal/repositorio"
)

func main() {
    // Carregar configuracao
    cfg := config.Carregar()
    fmt.Println("Porta:", cfg.Porta)

    // Usar repositorio
    repo := repositorio.NovoRepositorioUsuario()
    repo.Criar("Alice", "alice@exemplo.com")
    repo.Criar("Bob", "bob@exemplo.com")

    fmt.Println(repo)

    // Encontrar todos os usuarios
    for _, u := range repo.BuscarTodos() {
        fmt.Printf("  ID=%d, Nome=%s, Email=%s\n", u.ID, u.Nome, u.Email)
    }
}

Saida:

TEXT
Porta: 8080
RepositorioUsuario{2 usuarios no total}
  ID=1, Nome=Alice, Email=alice@exemplo.com
  ID=2, Nome=Bob, Email=bob@exemplo.com

❓ Perguntas Frequentes

P1: Nomes de pacotes e nomes de diretorios precisam corresponder?

Nao e forcado, mas fortemente recomendado. Convencoes do Go especificam que nomes de pacotes devem corresponder a nomes de diretorios. Podem diferir, mas causa confusao:

GO
// myutil/calc.go
package calculator // Nome do pacote nao corresponde ao nome do diretorio

// Importacao usa o caminho, mas uso usa o nome do pacote
import "my-module/myutil" // Caminho
calculator.Add(1, 2)      // Usa nome do pacote

P2: Posso usar qualquer coisa para o caminho do modulo em go mod init?

Sim, mas usar um caminho de repositorio e recomendado:

BASH
# ✅ Recomendado: facil para outros referenciar
go mod init github.com/seunome/seuprojeto

# ⚠️ Tambem serve: projetos locais
go mod init meuProjeto

# ❌ Evitar: caracteres especiais ou espacos
go mod init "meu projeto"

P3: Por que o arquivo go.sum e necessario?

go.sum registra o hash criptografico de cada dependencia, garantindo que o codigo baixado nao foi adulterado. Deve ser commitado no controle de versao junto com go.mod:

BASH
# go.mod — declara dependencias e versoes
# go.sum — verifica integridade da dependencia
# Ambos sao essenciais
git add go.mod go.sum

P4: Como organizar multiplos arquivos no mesmo pacote?

Todos os arquivos .go no mesmo diretorio compartilham o mesmo pacote e podem se referenciar diretamente sem importacao:

GO
// mathutil/add.go
package mathutil

func Add(a, b int) int { return a + b }

// mathutil/multiply.go — mesmo pacote, pode usar Add diretamente
package mathutil

func Multiply(a, b int) int {
    resultado := 0
    for i := 0; i < b; i++ {
        resultado = Add(resultado, a) // Chamada direta, sem importacao necessaria
    }
    return resultado
}

📖 Resumo

Ponto Chave Conteudo
Declaracao de pacote Todo arquivo .go deve declarar seu nome de pacote na primeira linha
Inicializacao de modulo go mod init <caminho-modulo> cria go.mod
Gerenciamento de dependencias go mod tidy limpa dependencias, go get instala pacotes de terceiros
Regras de exportacao Maiuscula = exportado, minuscula = apenas privado do pacote
Pacote interno Controle de acesso forcado pelo compilador, apenas importavel pela arvore de diretorios pai
Melhores praticas Nome do pacote corresponde ao nome do diretorio, organizar por responsabilidade, evitar dependencias circulares

📝 Exercicios

Exercicio 1: Criar um Pacote de Utilitarios de String

Criar um pacote stringutil com as seguintes funcoes exportadas:

Depois importar e usar em main.go.

Exercicio 2: Pratica com Pacote interno

Criar um projeto com um pacote internal/config para ler configuracao (como uma struct), e um pacote app que usa a configuracao. Verificar: o pacote app pode importar internal/config, mas pacotes externos nao podem.

Exercicio 3: Calculadora Multi-Pacote

Projetar um projeto de calculadora com os seguintes pacotes:

Requisitos: pacote cientifico pode chamar funcoes do pacote operacao, e toda logica de validacao vai em internal/validador.


Proxima Licao: Pratica com Struct →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%