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:
package main // Pacote de entrada para programas executaveis
package mathutil // Pacote utilitario
.go no mesmo diretorio devem declarar o mesmo nome de pacote (geralmente correspondendo ao nome do diretorio).
2. Inicializando um Modulo
# Executar no diretorio raiz do projeto
go mod init meu-modulo
# Gera o arquivo go.mod:
# module meu-modulo
#
# go 1.24
github.com/usuario/meu-modulo, facilitando a referencia por outros.
3. Limpando Dependencias
# Adicionar dependencias faltantes, remover nao utilizadas
go mod tidy
go mod tidy toda vez que introduzir um novo pacote de terceiros.
4. Declaracoes import
import "fmt" // Biblioteca padrao
import "github.com/gin-gonic/gin" // Pacote de terceiros
// Importacoes agrupadas (recomendado)
import (
"fmt"
"log"
"os"
)
5. Regras de Exportacao
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
}
6. Pacote interno
meuapp/
├── internal/
│ └── auth/ ← Apenas meuapp/ e seus subdiretorios podem importar
│ └── auth.go
└── cmd/
└── server/
└── main.go ← ✅ Pode importar meuapp/internal/auth
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
# 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
saudacao/saudacao.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:
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:
go run main.go
Saida:
Ola, Alice! Bem-vindo a programacao Go.
Tchau, Alice!
Exemplo: Usando Pacotes de Terceiros (Dificuldade ⭐⭐)
Inicializar projeto:
mkdir http-demo && cd http-demo
go mod init http-demo
go get github.com/gin-gonic/gin
main.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:
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)
go.mod:
module sistema-bancario
go 1.24
internal/validador/validador.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:
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:
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:
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:
go run main.go
Saida:
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:
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:
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:
[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:
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:
package model
// Usuario modelo
type Usuario struct {
ID int `json:"id"`
Nome string `json:"nome"`
Email string `json:"email"`
}
internal/repositorio/usuario_repo.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:
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:
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:
// 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:
# ✅ 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:
# 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:
// 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:
Inverter(s string) string— Inverter uma stringParaMaiusculas(s string) string— Converter para maiusculasContarPalavras(s string) int— Contar palavras
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:
calc/operacao— Operacoes basicas (somar, subtrair, multiplicar, dividir)calc/cientifico— Operacoes cientificas (potencia, fatorial)internal/validador— Validacao de entrada (verificacoes de divisao por zero, verificacoes de numeros negativos, etc.)
Requisitos: pacote cientifico pode chamar funcoes do pacote operacao, e toda logica de validacao vai em internal/validador.



