Tutoriais Go em Portugues
Tutoriais praticos com codigo completo para aprender Go construindo projetos reais.
Web e APIs
- API REST com Go — Construa uma API REST completa
- GraphQL com gqlgen — API GraphQL type-safe
- gRPC Tutorial — Comunicacao entre servicos
Banco de Dados
- PostgreSQL CRUD — Operacoes CRUD com pgx
- MongoDB — Driver oficial do MongoDB
- Redis Cache — Caching com Redis
- Elasticsearch — Busca full-text
DevOps e Infra
- Docker — Containerize sua app Go
- Kubernetes — Deploy no K8s
- AWS Deploy — Deploy na AWS
- Terraform — Infrastructure as Code
- Vault — Gestao de secrets
Arquitetura
- Microservices — Arquitetura de microservicos
- Clean Architecture — Projeto bem estruturado
- Concurrency Patterns — Padroes de concorrencia
Mensageria e Streaming
- Kafka Streaming — Eventos com Kafka
- RabbitMQ — Message broker
Observabilidade
- Prometheus — Metricas
- Observability — Tracing e logs
- Performance Profiling — pprof e benchmarks
Testes e CI/CD
- Testing — Testes em Go
- TDD e CI/CD — Desenvolvimento guiado por testes
Seguranca
Mais
- CLI Tools — Ferramentas de linha de comando
- Dapr — Runtime distribuido
- Temporal — Workflows duráveis
- MinIO — Object storage
Veja tambem: Aprenda Go do zero | Vagas Go | Empresas que usam Go
Go e HashiCorp Vault: Gerenciamento de Segredos
Implemente gerenciamento seguro de segredos com HashiCorp Vault em aplicações Go. Aprenda autenticação, políticas, secrets dinâmicos e integração completa.
Go e HashiCorp Vault: Gerenciamento de Segredos
HashiCorp Vault é a solução líder para gerenciamento de segredos em ambientes cloud-native. Sua integração com Go permite proteger credenciais, APIs keys, certificados e dados sensíveis de forma segura e auditável.
Neste guia completo, você aprenderá a integrar Vault com aplicações Go, implementar padrões de segurança, usar secrets dinâmicos e automatizar rotação de credenciais.
Índice
- Fundamentos do Vault
- Instalação e Configuração
- Cliente Go do Vault
- Autenticação
- Leitura e Escrita de Secrets
- Secrets Dinâmicos
- Rotação Automática
- Padrões de Produção
Fundamentos do Vault
Arquitetura
┌─────────────────────────────────────────────────────────────┐
│ Aplicação Go │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Vault Client Go │ │
│ │ • Autenticação │ │
│ │ • Cache de tokens │ │
│ │ • Renovação automática │ │
│ └─────────────────────────┬───────────────────────────┘ │
└────────────────────────────┼────────────────────────────────┘
│ HTTPS/TLS
▼
┌─────────────────────────────────────────────────────────────┐
│ Vault Server │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Autenticação│ │ Policies │ │ Secret │ │
│ │ Methods │ │ (ACL) │ │ Engines │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Transit │ │ Database │ │ PKI │ │
│ │ (Encryption)│ │ (Dynamic) │ │ (Certificates) │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
Key Features
1. Secrets Estáticos Key-Value store para credenciais, API keys, certificados.
Go e Kafka: Processamento de Streaming
Aprenda a usar Apache Kafka com Go para streaming de eventos. Tutorial completo com exemplos práticos de producers, consumers, topics, partitions e padrões de processamento.
Go e Kafka: Processamento de Streaming
Apache Kafka é a plataforma de streaming de eventos mais popular do mundo, usada por milhares de empresas para processar trilhões de eventos diariamente. Com Go, você pode construir aplicações de streaming de alta performance e baixa latência.
Neste guia completo, você vai aprender a usar Kafka com Go desde o básico até padrões avançados de processamento.
O Que Você Vai Aprender
- Fundamentos do Apache Kafka
- Configuração do cliente Kafka em Go
- Implementação de producers e consumers
- Gerenciamento de topics e partitions
- Consumer groups para escalabilidade
- Tratamento de erros e retries
- Casos de uso do mundo real
Por Que Kafka?
O Problema: Processamento em Lote vs Streaming
Tradicionalmente, sistemas processavam dados em lotes (batch):
Go e AWS: Deploy de Aplicações na Nuvem
Guia completo de deployment de aplicações Go na AWS: EC2, Lambda, S3, RDS e melhores práticas de infraestrutura em nuvem
Go e AWS: Deploy de Aplicações na Nuvem
A AWS (Amazon Web Services) é a maior plataforma de cloud computing do mundo, oferecendo mais de 200 serviços. Quando combinada com Go, uma linguagem compilada com excelente performance e baixo consumo de recursos, você obtém uma stack poderosa e econômica para aplicações em produção.
Neste guia completo, você aprenderá desde os conceitos básicos até técnicas avançadas para deploy de aplicações Go na AWS, incluindo EC2, Lambda, S3, RDS e muito mais.
Go e Dapr: Distributed Application Runtime
Simplifique microsserviços com Dapr em Go. Aprenda service invocation, state management, pub/sub e bindings com exemplos práticos.
Go e Dapr: Distributed Application Runtime
Dapr (Distributed Application Runtime) é uma plataforma portátil e event-driven que facilita a construção de aplicações distribuídas e microsserviços. Projetado pela Microsoft, é usado por empresas como Alibaba, Tencent e Ignition Group.
Neste guia, você aprenderá a usar Dapr com Go para resolver desafios comuns de microsserviços de forma simples e portátil.
Índice
- O que é Dapr?
- Arquitetura e Sidecar
- Configuração
- Service Invocation
- State Management
- Pub/Sub Messaging
- Bindings
- Observability
O que é Dapr?
Building Blocks
Dapr fornece 8 building blocks que resolvem problemas comuns:
Go e MinIO: Object Storage com S3-Compatible API
Aprenda a usar MinIO em Go para armazenamento de objetos. Guia completo de buckets, uploads, presigned URLs e padrões de produção com SDK AWS S3.
Go e MinIO: Object Storage com S3-Compatible API
MinIO é o servidor de object storage de código aberto mais popular, compatível com a API S3 da AWS. Escrito em Go, oferece alta performance e é ideal para aplicações que precisam de storage escalável sem depender de serviços cloud proprietários.
Neste guia, você aprenderá a integrar MinIO com aplicações Go, desde operações básicas até padrões avançados de produção.
Índice
- Por que MinIO?
- Configuração do Ambiente
- SDK AWS S3 para Go
- Operações Básicas
- Uploads Avançados
- Presigned URLs
- Streaming e Large Objects
- Padrões de Produção
Por que MinIO?
Vantagens
1. Compatibilidade S3 100% compatível com API S3 da AWS. Mesmo código funciona com MinIO, AWS S3, ou qualquer outro storage S3-compatible.
Go e Open Policy Agent (OPA): Policy as Code
Implemente authorization e policy management com OPA em Go. Aprenda Rego language, Go SDK e padrões de Policy as Code para microsserviços.
Go e Open Policy Agent (OPA): Policy as Code
Open Policy Agent (OPA) é um motor de políticas open-source que permite unificar policy management em toda a stack. Desde API authorization até Kubernetes admission control, OPA proporciona uma linguagem declarativa (Rego) para definir políticas.
Neste guia, você aprenderá a integrar OPA com aplicações Go para implementar authorization flexível e auditável.
Índice
- O que é OPA?
- Linguagem Rego
- Go SDK
- API Authorization
- Policy Testing
- Integração com Middleware
- Bundles e Atualização Dinâmica
O que é OPA?
Casos de Uso
- API Authorization: Permitir/negar acesso a endpoints
- Data Filtering: Filtrar dados baseado em permissões
- Kubernetes: Admission control policies
- Terraform: Policy enforcement para infraestrutura
- Service Mesh: Envoy/Istio authorization
Arquitetura
┌─────────────────────────────────────────────────────────────┐
│ Aplicação Go │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ HTTP Request + JWT │ │
│ │ {user: "alice", action: "read", resource: "document"}│ │
│ └──────────────────────┬──────────────────────────────┘ │
│ │ Query │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ OPA (via Go SDK) │ │
│ │ ┌──────────────┐ ┌───────────────────────┐ │ │
│ │ │ Rego Policy │ │ Data │ │ │
│ │ │ │ │ {users, roles, etc} │ │ │
│ │ │ allow { ... }│ │ │ │ │
│ │ └──────────────┘ └───────────────────────┘ │ │
│ └──────────────────────┬──────────────────────────────┘ │
│ │ Result: true/false │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Response (Allow/Deny) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Linguagem Rego
Fundamentos
# policy/authz.rego
package authz
# Importa dados
default allow := false
# Regra básica: admin pode tudo
allow {
input.user.role == "admin"
}
# Regra: owner pode acessar seus próprios recursos
allow {
input.user.id == input.resource.owner_id
}
# Regra: usuários com permissão específica
allow {
permission := data.permissions[input.user.id][_]
permission.action == input.action
permission.resource == input.resource.type
}
# Helper: check role
check_role(role) {
input.user.role == role
}
Políticas de API
# policy/api.rego
package api
import future.keywords.if
import future.keywords.in
# Default deny
default allow := false
# GET /users - apenas admins ou usuários autenticados
allow if {
input.method == "GET"
input.path == ["users"]
input.user.authenticated
}
# GET /users/:id - próprio usuário ou admin
allow if {
input.method == "GET"
input.path == ["users", user_id]
input.user.id == user_id
}
allow if {
input.method == "GET"
input.path == ["users", _]
input.user.role == "admin"
}
# POST /orders - qualquer usuário autenticado
allow if {
input.method == "POST"
input.path == ["orders"]
input.user.authenticated
}
# Rate limiting check
rate_limit_ok if {
not data.rate_limits[input.user.id].count > 100
}
# Composição de políticas
allow if {
check_permission
rate_limit_ok
business_hours
}
check_permission if {
some permission in data.permissions[input.user.role]
permission.resource == input.resource
permission.action == input.action
}
business_hours if {
to_number(input.time) >= 9
to_number(input.time) <= 18
}
Go SDK
Instalação
go get github.com/open-policy-agent/opa/rego
go get github.com/open-policy-agent/opa/storage/inmem
Uso Básico
package opa
import (
"context"
"embed"
"fmt"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/storage/inmem"
)
//go:embed policy/*.rego
var policyFS embed.FS
type Authorizer struct {
query rego.PreparedEvalQuery
}
func NewAuthorizer() (*Authorizer, error) {
// Carrega políticas do embed.FS
policy, err := policyFS.ReadFile("policy/authz.rego")
if err != nil {
return nil, err
}
// Compila e prepara query
r := rego.New(
rego.Query("data.authz.allow"),
rego.Module("authz.rego", string(policy)),
)
query, err := r.PrepareForEval(context.Background())
if err != nil {
return nil, fmt.Errorf("falha ao compilar políticas: %w", err)
}
return &Authorizer{query: query}, nil
}
func (a *Authorizer) Authorize(ctx context.Context, input map[string]interface{}) (bool, error) {
results, err := a.query.Eval(ctx, rego.EvalInput(input))
if err != nil {
return false, err
}
if len(results) == 0 {
return false, nil
}
// Extrai resultado booleano
allowed, ok := results[0].Expressions[0].Value.(bool)
if !ok {
return false, fmt.Errorf("resultado inválido")
}
return allowed, nil
}
Com Dados Dinâmicos
package opa
import (
"context"
"encoding/json"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/storage"
"github.com/open-policy-agent/opa/storage/inmem"
)
type PolicyEngine struct {
store storage.Store
}
func NewPolicyEngine() *PolicyEngine {
return &PolicyEngine{
store: inmem.NewFromObject(map[string]interface{}{}),
}
}
func (pe *PolicyEngine) LoadData(ctx context.Context, path string, data interface{}) error {
jsonData, err := json.Marshal(data)
if err != nil {
return err
}
var jsonValue interface{}
if err := json.Unmarshal(jsonData, &jsonValue); err != nil {
return err
}
txn := storage.NewTransactionOrDie(ctx, pe.store, storage.WriteParams)
defer pe.store.Abort(ctx, txn)
if err := pe.store.Write(ctx, txn, storage.AddOp, storage.MustParsePath(path), jsonValue); err != nil {
return err
}
return pe.store.Commit(ctx, txn)
}
func (pe *PolicyEngine) Evaluate(ctx context.Context, query string, input interface{}) (rego.ResultSet, error) {
r := rego.New(
rego.Query(query),
rego.Store(pe.store),
rego.Input(input),
)
return r.Eval(ctx)
}
API Authorization
Middleware de Autorização
package middleware
import (
"context"
"encoding/json"
"net/http"
"strings"
"myapp/opa"
)
type contextKey string
const userContextKey contextKey = "user"
type AuthMiddleware struct {
authorizer *opa.Authorizer
}
func NewAuthMiddleware(authorizer *opa.Authorizer) *AuthMiddleware {
return &AuthMiddleware{authorizer: authorizer}
}
func (m *AuthMiddleware) Authorize(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Extrai usuário do context (setado por auth middleware anterior)
user, ok := r.Context().Value(userContextKey).(User)
if !ok {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Prepara input para OPA
input := map[string]interface{}{
"method": r.Method,
"path": splitPath(r.URL.Path),
"user": user,
"headers": r.Header,
"time": getCurrentHour(),
}
// Avalia política
allowed, err := m.authorizer.Authorize(r.Context(), input)
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
if !allowed {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
func splitPath(path string) []string {
return strings.Split(strings.Trim(path, "/"), "/")
}
Uso em Handlers
package main
import (
"net/http"
"myapp/middleware"
"myapp/opa"
)
func main() {
authorizer, _ := opa.NewAuthorizer()
authMiddleware := middleware.NewAuthMiddleware(authorizer)
mux := http.NewServeMux()
// Protege rotas
mux.Handle("/api/", authMiddleware.Authorize(apiHandler()))
mux.Handle("/admin/", authMiddleware.Authorize(adminHandler()))
http.ListenAndServe(":8080", mux)
}
Policy Testing
Testes em Rego
# policy/api_test.rego
package api
test_allow_get_users_if_admin {
allow with input as {
"method": "GET",
"path": ["users"],
"user": {"role": "admin", "authenticated": true}
}
}
test_deny_get_users_if_not_authenticated {
not allow with input as {
"method": "GET",
"path": ["users"],
"user": {"authenticated": false}
}
}
test_allow_get_own_user {
allow with input as {
"method": "GET",
"path": ["users", "user-123"],
"user": {"id": "user-123", "authenticated": true}
}
}
test_deny_access_outside_business_hours {
not allow with input as {
"method": "POST",
"path": ["orders"],
"user": {"authenticated": true},
"time": 22
}
}
Testes em Go
package opa_test
import (
"context"
"testing"
"myapp/opa"
)
func TestAuthorizer(t *testing.T) {
auth, err := opa.NewAuthorizer()
if err != nil {
t.Fatal(err)
}
tests := []struct {
name string
input map[string]interface{}
expected bool
}{
{
name: "admin_can_access_anything",
input: map[string]interface{}{
"method": "DELETE",
"path": []string{"users", "123"},
"user": map[string]interface{}{"role": "admin"},
},
expected: true,
},
{
name: "user_can_access_own_resource",
input: map[string]interface{}{
"method": "GET",
"path": []string{"users", "user-123"},
"user": map[string]interface{}{
"id": "user-123",
},
"resource": map[string]interface{}{
"owner_id": "user-123",
},
},
expected: true,
},
{
name: "user_cannot_access_others_resource",
input: map[string]interface{}{
"method": "GET",
"path": []string{"users", "user-456"},
"user": map[string]interface{}{
"id": "user-123",
},
"resource": map[string]interface{}{
"owner_id": "user-456",
},
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := auth.Authorize(context.Background(), tt.input)
if err != nil {
t.Fatal(err)
}
if result != tt.expected {
t.Errorf("expected %v, got %v", tt.expected, result)
}
})
}
}
Bundles e Atualização Dinâmica
OPA Server
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"github.com/open-policy-agent/opa/sdk"
)
func main() {
ctx := context.Background()
// Configura OPA com bundle remoto
config := []byte(`
{
"services": {
"bundle_service": {
"url": "https://bundles.example.com",
"credentials": {
"bearer": {
"token": "${TOKEN}"
}
}
}
},
"bundles": {
"authz": {
"service": "bundle_service",
"resource": "bundles/authz.tar.gz",
"polling": {
"min_delay_seconds": 60,
"max_delay_seconds": 120
}
}
}
}
`)
opa, err := sdk.New(ctx, sdk.Options{
ID: "opa-instance",
Config: bytes.NewReader(config),
})
if err != nil {
log.Fatal(err)
}
defer opa.Stop(ctx)
// Usa OPA para queries
if result, err := opa.Decision(ctx, sdk.DecisionOptions{
Path: "authz/allow",
Input: map[string]interface{}{"user": "alice"},
}); err != nil {
log.Fatal(err)
} else {
log.Printf("Decision: %v", result.Result)
}
// Aguarda sinal de término
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
<-sig
}
Hot Reload
package opa
import (
"context"
"sync"
"time"
"github.com/fsnotify/fsnotify"
"github.com/open-policy-agent/opa/rego"
)
type HotReloadAuthorizer struct {
mu sync.RWMutex
query rego.PreparedEvalQuery
policyDir string
}
func NewHotReloadAuthorizer(policyDir string) (*HotReloadAuthorizer, error) {
ha := &HotReloadAuthorizer{policyDir: policyDir}
if err := ha.reload(); err != nil {
return nil, err
}
// Inicia watcher
go ha.watch()
return ha, nil
}
func (ha *HotReloadAuthorizer) reload() error {
// Carrega e compila políticas
modules := make(map[string]string)
// ... carrega arquivos .rego
r := rego.New(
rego.Query("data.authz.allow"),
rego.LoadedModules(modules),
)
query, err := r.PrepareForEval(context.Background())
if err != nil {
return err
}
ha.mu.Lock()
ha.query = query
ha.mu.Unlock()
return nil
}
func (ha *HotReloadAuthorizer) watch() {
watcher, _ := fsnotify.NewWatcher()
defer watcher.Close()
watcher.Add(ha.policyDir)
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
if event.Op&fsnotify.Write == fsnotify.Write {
// Recarrega após delay (debounce)
time.Sleep(100 * time.Millisecond)
ha.reload()
}
}
}
}
func (ha *HotReloadAuthorizer) Authorize(ctx context.Context, input map[string]interface{}) (bool, error) {
ha.mu.RLock()
query := ha.query
ha.mu.RUnlock()
results, err := query.Eval(ctx, rego.EvalInput(input))
// ... processa resultado
}
Conclusão
Neste guia, você aprendeu:
Go e Prometheus: Métricas e Alertas em Produção
Implemente métricas e alertas com Prometheus em aplicações Go. Guia completo de instrumentação, Grafana dashboards e alerting com exemplos práticos.
Go e Prometheus: Métricas e Alertas em Produção
Prometheus tornou-se o padrão de facto para monitoramento de aplicações cloud-native. Sua combinação com Go é natural - ambos foram criados pela SoundCloud e compartilham filosofias de simplicidade e performance.
Neste guia, você aprenderá a instrumentar aplicações Go com métricas Prometheus, criar dashboards no Grafana e configurar alertas inteligentes.
Índice
- Fundamentos do Prometheus
- Bibliotecas Go para Prometheus
- Tipos de Métricas
- Instrumentando Aplicações
- Métricas de Runtime Go
- Tracing e Context
- Dashboards no Grafana
- Alertas com Alertmanager
- Padrões de Produção
Fundamentos do Prometheus
Arquitetura
┌─────────────────────────────────────────────────────────────┐
│ Aplicações Go │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ App 1 │ │ App 2 │ │ App 3 │ │
│ │ :8080/metrics│ │ :8081/metrics│ │ :8082/metrics│ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
└─────────┼────────────────┼────────────────┼─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Prometheus Server │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Retriever │ │ TSDB │ │ Query Engine │ │
│ │ (Pull) │ │ (Storage) │ │ (PromQL) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└──────────┬──────────────────────────────────────────────────┘
│
│ HTTP
▼
┌─────────────────────────────────────────────────────────────┐
│ Grafana │
│ (Visualização e Dashboards) │
└─────────────────────────────────────────────────────────────┘
│
│ Webhook
▼
┌─────────────────────────────────────────────────────────────┐
│ Alertmanager │
│ (Roteamento e Notificações) │
└─────────────────────────────────────────────────────────────┘
Modelo de Dados
Prometheus usa um modelo de dados dimensional:
Go e Temporal: Workflows Resilientes e Duráveis
Construa workflows resilientes com Temporal em Go. Aprenda about retries, sagas, signals e padrões de orquestração de processos distribuídos.
Go e Temporal: Workflows Resilientes e Duráveis
Temporal é uma plataforma de orquestração de workflows que permite criar processos de negócio resilientes, duráveis e escaláveis. Desenvolvido pelos criadores do Cadence (Uber), é amplamente adotado por empresas como Netflix, Stripe e Shopify.
Neste guia, você aprenderá a construir workflows complexos em Go com garantias de execução, retries automáticos e compensação (Saga pattern).
Índice
- Conceitos Fundamentais
- Configuração do Ambiente
- Primeiro Workflow
- Activities e Retries
- Saga Pattern
- Signals e Queries
- Timers e Cron Jobs
- Padrões Avançados
Conceitos Fundamentais
Arquitetura Temporal
┌─────────────────────────────────────────────────────────────┐
│ Temporal Server │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Frontend │ │ History │ │ Matching │ │
│ │ (gRPC) │ │ (Events) │ │ (Tasks) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ Persistence: PostgreSQL / MySQL / Cassandra / Elasticsearch│
└─────────────────────────────────────────────────────────────┘
│
│ gRPC
▼
┌─────────────────────────────────────────────────────────────┐
│ Workers (Go) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Workflow │ │ Activity │ │ Activity │ │
│ │ Engine │ │ Executor │ │ Executor │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ Workflows são determinísticos (replayable) │
│ Activities contêm side-effects (I/O, APIs) │
└─────────────────────────────────────────────────────────────┘
Conceitos Chave
Workflows: Funções Go que definem a lógica de negócio. São determinísticas e podem ser “replayed” indefinidamente.
Go e Terraform: Infrastructure as Code com Go
Aprenda a criar providers Terraform personalizados em Go para automatizar sua infraestrutura. Guia completo com exemplos práticos e padrões de produção.
Go e Terraform: Infrastructure as Code com Go
O Terraform revolucionou a forma como gerenciamos infraestrutura, permitindo definir recursos como código. Embora o Terraform use sua própria linguagem de configuração (HCL), a linguagem Go é fundamental para estender suas capacidades através de providers personalizados.
Neste guia completo, você aprenderá a criar providers Terraform em Go, integrar Terraform com aplicações Go, e automatizar infraestrutura usando as melhores práticas de produção.
Índice
- Por que Go e Terraform?
- Arquitetura de Providers Terraform
- Criando seu Primeiro Provider
- Gerenciando Recursos com Go
- Testando Providers
- Integração com Aplicações Go
- Padrões de Produção
- Deploy e Distribuição
Por que Go e Terraform?
Vantagens da Combinação
1. Performance Nativa Providers Terraform são executados como binários nativos, e Go oferece excelente performance com compilação estática.
Go GraphQL: Criando APIs com gqlgen
Aprenda a construir APIs GraphQL robustas em Go usando gqlgen. Tutorial completo com exemplos práticos, schema definition, resolvers, mutations e subscriptions.
Go GraphQL: Criando APIs com gqlgen
GraphQL revolucionou a forma como construímos APIs, oferecendo uma alternativa flexível ao REST tradicional. Com Go e gqlgen, você pode criar APIs GraphQL de alto desempenho com type-safety e excelente experiência de desenvolvimento.
Neste guia completo, você vai aprender a construir uma API GraphQL do zero usando gqlgen, o framework mais popular para GraphQL em Go.
O Que Você Vai Aprender
- Diferenças entre GraphQL e REST
- Configuração do gqlgen
- Definição de schema GraphQL
- Implementação de resolvers
- Mutations e queries
- Subscriptions em tempo real
- Testes de APIs GraphQL
GraphQL vs REST: Por Que Migrar?
Problemas do REST
GET /users/1
GET /users/1/posts
GET /users/1/posts/123/comments
No REST, você frequentemente precisa de múltiplas requisições para obter dados relacionados:
Go e gRPC: Comunicação entre Serviços Completo
Aprenda a criar APIs eficientes com gRPC em Go. Tutorial completo cobrindo Protocol Buffers, streaming, interceptores e testes.
Go e gRPC: Comunicação entre Serviços
O gRPC tornou-se o padrão de facto para comunicação entre microserviços em ambientes de alta performance. Desenvolvido pelo Google, ele oferece vantagens significativas sobre REST tradicional, especialmente quando combinado com a eficiência do Go.
Neste tutorial completo, você vai aprender a construir serviços gRPC robustos em Go, desde o básico até técnicas avançadas como streaming bidirecional e interceptores.
Por Que Usar gRPC em Go?
gRPC vs REST: Comparativo
| Característica | REST/HTTP JSON | gRPC |
|---|---|---|
| Formato | JSON (texto) | Protocol Buffers (binário) |
| Performance | ~15x mais lento | ~15x mais rápido |
| Payload | Verbos, repetitivo | Compacto, eficiente |
| Tipagem | Fraca (runtime) | Forte (compile-time) |
| Streaming | Complicado (SSE/WebSocket) | Nativo e simples |
| Code Generation | Manual | Automático |
| Browser Support | Nativo | Requer gRPC-Web |
Quando escolher gRPC:
Go e Elasticsearch: Busca Full-Text Avançada
Aprenda a implementar busca full-text poderosa com Go e Elasticsearch. Guia completo de indexação, queries, agregações e casos de uso reais.
Go e Elasticsearch: Busca Full-Text Avançada
Elasticsearch é o motor de busca mais popular do mundo, usado desde pequenas aplicações até sistemas como Netflix e Uber. Neste guia, você aprenderá a integrar Elasticsearch com Go para criar buscas rápidas, relevantes e escaláveis.
Índice
- O que é Elasticsearch?
- Configuração do Cliente Go
- Indexando Documentos
- Realizando Buscas
- Queries Avançadas
- Agregações
- Mapeamentos e Análise
- Exemplo Prático: Catálogo de Produtos
- Performance e Otimização
O que é Elasticsearch?
Elasticsearch é um motor de busca e análise distribuído baseado no Apache Lucene. Ele proporciona:
Go Security: Boas Práticas de Segurança
Aprenda boas práticas de segurança para aplicações Go. Guia completo sobre validação de input, autenticação, autorização, vulnerabilidades comuns e OWASP para Go.
Go Security: Boas Práticas de Segurança
Introdução
Segurança em aplicações Go é fundamental para proteger dados sensíveis, prevenir ataques e garantir conformidade com regulamentações como LGPD e GDPR. Embora Go seja uma linguagem segura por design (memory safety, type safety), vulnerabilidades de segurança ainda ocorrem principalmente devido a práticas de codificação inadequadas.
Neste guia, você vai aprender as melhores práticas de segurança para desenvolver aplicações Go robustas e protegidas contra as ameaças mais comuns.
Go e RabbitMQ: Mensageria Assíncrona Completa
Aprenda a usar RabbitMQ com Go para mensageria assíncrona. Tutorial completo com exemplos práticos de publishers, consumers, exchanges e padrões de mensageria.
Go e RabbitMQ: Mensageria Assíncrona Completa
Introdução
RabbitMQ é um dos brokers de mensagens mais populares do mundo, usado por empresas como Uber, Reddit e Stripe para processar bilhões de mensagens diariamente. Quando combinado com Go, criamos sistemas altamente performáticos, confiáveis e escaláveis.
Neste guia completo, você vai aprender desde os conceitos fundamentais até padrões avançados de mensageria com Go e RabbitMQ.
O que é RabbitMQ?
RabbitMQ é um message broker (corretor de mensagens) de código aberto que implementa o protocolo AMQP (Advanced Message Queuing Protocol). Ele atua como um intermediário entre aplicações, permitindo comunicação assíncrona e desacoplada.
Go e Kubernetes: Deploy de Containers
Guia completo para fazer deploy de aplicações Go em Kubernetes. Aprenda a containerizar apps Go, criar manifests K8s e implementar health checks.
Go e Kubernetes: Deploy de Containers
Neste guia completo, você aprenderá a fazer deploy de aplicações Go em Kubernetes, desde a containerização até a configuração de health checks e boas práticas de produção. Se você está construindo microserviços ou APIs REST em Go, o Kubernetes é a plataforma ideal para orquestrar seus containers.
Por que Kubernetes para Go?
Go foi projetada para a era dos containers. Sua compilação para binário único, baixo consumo de memória e startup instantâneo tornam aplicações Go perfeitas para Kubernetes:
Go CLI: Criando Ferramentas de Linha de Comando Poderosas
Aprenda a construir CLI tools profissionais em Go. Domine a biblioteca Cobra, gerenciamento de flags, subcomandos, arquivos de configuração e crie ferramentas de linha de comando com excelente UX.
Ferramentas de linha de comando (CLI) são essenciais no arsenal de qualquer desenvolvedor. Desde gerenciadores de pacotes como npm e pip até infraestrutura como kubectl e terraform, CLIs bem projetadas aumentam a produtividade exponencialmente. Go é a linguagem perfeita para criar CLIs rápidas, portáteis e eficientes.
Neste guia completo, você aprenderá a construir CLI tools profissionais usando Go, desde flags simples até ferramentas complexas com subcomandos e auto-complete.
Por Que Go para CLI Tools?
Vantagens Competitivas
┌─────────────────────────────────────────────────────────────────┐
│ CARACTERÍSTICA │ GO │ PYTHON │ NODE.JS │ RUST │
├─────────────────────────────────────────────────────────────────┤
│ Startup │ ~5ms │ ~100ms │ ~200ms │ ~10ms │
├─────────────────────────────────────────────────────────────────┤
│ Binário único │ ✅ │ ❌ │ ❌ │ ✅ │
├─────────────────────────────────────────────────────────────────┤
│ Cross-compile │ ✅ │ ❌ │ ❌ │ ✅ │
├─────────────────────────────────────────────────────────────────┤
│ Memória │ Baixa │ Alta │ Média │ Baixa │
├─────────────────────────────────────────────────────────────────┤
│ Curva de aprend. │ Média │ Baixa │ Baixa │ Alta │
└─────────────────────────────────────────────────────────────────┘
Casos de Sucesso
- Docker: Container runtime escrito em Go
- Kubernetes: Orquestração de containers
- Hugo: Gerador de sites estáticos
- Terraform: Infrastructure as Code
- Cobra: Framework CLI usado por Kubernetes, etcd, e muitos outros
Fundamentos de CLI em Go
Flags Nativas com flag Package
Go inclui um pacote flag na biblioteca padrão para parsing de argumentos:
Go e Docker: Containerização de Aplicações com Multi-Stage Builds
Aprenda a containerizar aplicações Go com Docker. Guia completo de Dockerfiles otimizados, multi-stage builds, Docker Compose para desenvolvimento, boas práticas de produção e segurança. Reduza imagens de 1GB para 15MB.
Go e Docker são uma combinação poderosa. Enquanto aplicações em outras linguagens precisam de runtimes pesados, Go compila para binários nativos que rodam em containers minimalistas de 10-20MB. Neste guia, você vai aprender a criar imagens Docker otimizadas, seguras e prontas para produção.
Por Que Docker com Go?
Vantagens da Combinação
| Aspecto | Go + Docker | Outras Linguagens |
|---|---|---|
| Tamanho da Imagem | 10-50MB | 100MB-1GB+ |
| Tempo de Startup | < 100ms | 1-30 segundos |
| Memory Footprint | 10-50MB | 100MB-1GB |
| Runtime | Nenhum (binário nativo) | JVM, Node, Python |
| Security Surface | Mínima (scratch/alpine) | Grande (SO completo) |
Casos de Uso
- Microserviços: Deploy rápido e escalável
- CI/CD: Builds reproduzíveis
- Desenvolvimento: Ambiente consistente
- Produção: Alta densidade de containers
Dockerfile Básico para Go
O Problema: Dockerfile Inicial
# ❌ NÃO FAÇA ISSO - Imagem gigante (~1GB)
FROM golang:1.21
WORKDIR /app
COPY . .
RUN go build -o main .
CMD ["./main"]
Problemas:
Go e MongoDB: CRUD e Agregações Completas
Aprenda a integrar Go com MongoDB de forma profissional. Domine operações CRUD, aggregation pipeline, indexing, transações e padrões de produção com o driver oficial.
MongoDB é o banco de dados NoSQL mais popular do mundo, ideal para aplicações modernas que precisam de flexibilidade e escalabilidade. Combinado com Go, você tem uma stack poderosa para lidar com dados não estruturados e semi-estruturados. Neste guia, você aprenderá tudo sobre MongoDB em Go, desde operações básicas até agregações complexas.
Por Que MongoDB com Go?
Quando Escolher MongoDB
┌─────────────────────────────────────────────────────────────────┐
│ MONGODB É IDEAL PARA: │
├─────────────────────────────────────────────────────────────────┤
│ ✅ Dados flexíveis e em evolução │
│ ✅ Alta velocidade de escrita │
│ ✅ Escalabilidade horizontal (sharding) │
│ ✅ Documentos JSON-like nativos │
│ ✅ Geolocalização e queries espaciais │
│ ✅ Prototipagem rápida │
├─────────────────────────────────────────────────────────────────┤
│ MONGODB + GO = Type-safe + Flexibilidade │
└─────────────────────────────────────────────────────────────────┘
MongoDB vs PostgreSQL com Go
| Aspecto | MongoDB | PostgreSQL |
|---|---|---|
| Esquema | Flexível (schema-less) | Rígido (schema-full) |
| Relacionamentos | Embutidos ou referências | JOINs nativos |
| Escalabilidade | Horizontal (sharding) | Vertical + Replicação |
| Casos de uso | CMS, IoT, Analytics | Financeiro, ERP, ACID |
| Performance | Excelente para reads | Excelente para ACID |
Configuração do Projeto
Instalação do Driver
# Driver oficial MongoDB para Go
go get go.mongodb.org/mongo-driver/v2/mongo
# BSON utilities
go get go.mongodb.org/mongo-driver/v2/bson
# Options e configs
go get go.mongodb.org/mongo-driver/v2/mongo/options
Estrutura do Projeto
mongo-project/
├── cmd/
│ └── api/
│ └── main.go
├── internal/
│ ├── config/
│ │ └── mongodb.go
│ ├── models/
│ │ └── user.go
│ ├── repository/
│ │ └── user_repository.go
│ └── service/
│ └── user_service.go
├── go.mod
└── .env
Conexão com MongoDB
Configuração da Conexão
// internal/config/mongodb.go
package config
import (
"context"
"fmt"
"log"
"os"
"time"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
// MongoConfig armazena configurações de conexão
type MongoConfig struct {
URI string
Database string
Timeout time.Duration
}
// NewMongoConfig cria configuração a partir de variáveis de ambiente
func NewMongoConfig() *MongoConfig {
return &MongoConfig{
URI: getEnv("MONGODB_URI", "mongodb://localhost:27017"),
Database: getEnv("MONGODB_DATABASE", "myapp"),
Timeout: 10 * time.Second,
}
}
// Connect estabelece conexão com MongoDB
func (cfg *MongoConfig) Connect() (*mongo.Client, error) {
ctx, cancel := context.WithTimeout(context.Background(), cfg.Timeout)
defer cancel()
// Configurar opções do cliente
clientOptions := options.Client().
ApplyURI(cfg.URI).
SetMaxPoolSize(100).
SetMinPoolSize(10).
SetMaxConnIdleTime(30 * time.Second).
SetServerSelectionTimeout(5 * time.Second)
// Conectar
client, err := mongo.Connect(clientOptions)
if err != nil {
return nil, fmt.Errorf("falha ao conectar ao MongoDB: %w", err)
}
// Verificar conexão
if err := client.Ping(ctx, nil); err != nil {
return nil, fmt.Errorf("falha ao ping MongoDB: %w", err)
}
log.Println("✅ Conectado ao MongoDB")
return client, nil
}
// GetCollection retorna uma coleção específica
func (cfg *MongoConfig) GetCollection(client *mongo.Client, name string) *mongo.Collection {
return client.Database(cfg.Database).Collection(name)
}
// Disconnect fecha a conexão gracefully
func Disconnect(client *mongo.Client) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := client.Disconnect(ctx); err != nil {
return fmt.Errorf("falha ao desconectar: %w", err)
}
log.Println("🔌 Desconectado do MongoDB")
return nil
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
String de Conexão (Connection String)
// Exemplos de URI para diferentes cenários
// Local
mongodb://localhost:27017/myapp
// Com autenticação
mongodb://user:password@localhost:27017/myapp?authSource=admin
// Replica Set
mongodb://user:password@host1:27017,host2:27017,host3:27017/myapp?replicaSet=rs0
// MongoDB Atlas (cloud)
mongodb+srv://user:password@cluster.mongodb.net/myapp?retryWrites=true&w=majority
// Com opções adicionais
mongodb://localhost:27017/myapp?maxPoolSize=100&serverSelectionTimeoutMS=5000
Modelos e Estruturas
Definindo Documentos
// internal/models/user.go
package models
import (
"time"
"go.mongodb.org/mongo-driver/v2/bson/primitive"
)
// User representa um usuário no MongoDB
type User struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id"`
Email string `bson:"email" json:"email" validate:"required,email"`
Name string `bson:"name" json:"name" validate:"required,min=3,max=100"`
Password string `bson:"password" json:"-"` // Não serializar
Profile UserProfile `bson:"profile" json:"profile"`
Roles []string `bson:"roles" json:"roles"`
Active bool `bson:"active" json:"active"`
Tags []string `bson:"tags,omitempty" json:"tags,omitempty"`
Metadata map[string]interface{} `bson:"metadata,omitempty" json:"metadata,omitempty"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`
}
// UserProfile dados aninhados
type UserProfile struct {
Bio string `bson:"bio,omitempty" json:"bio,omitempty"`
Avatar string `bson:"avatar,omitempty" json:"avatar,omitempty"`
BirthDate time.Time `bson:"birth_date,omitempty" json:"birth_date,omitempty"`
Location string `bson:"location,omitempty" json:"location,omitempty"`
Website string `bson:"website,omitempty" json:"website,omitempty"`
}
// UserCreate DTO para criação
type UserCreate struct {
Email string `json:"email" validate:"required,email"`
Name string `json:"name" validate:"required,min=3,max=100"`
Password string `json:"password" validate:"required,min=8"`
Profile UserProfile `json:"profile,omitempty"`
Tags []string `json:"tags,omitempty"`
}
// UserUpdate DTO para atualização parcial
type UserUpdate struct {
Name *string `json:"name,omitempty"`
Email *string `json:"email,omitempty"`
Profile *UserProfile `json:"profile,omitempty"`
Roles []string `json:"roles,omitempty"`
Tags []string `json:"tags,omitempty"`
Active *bool `json:"active,omitempty"`
}
// CollectionName retorna o nome da coleção
func (User) CollectionName() string {
return "users"
}
Trabalhando com ObjectID
// Criar novo ObjectID
id := primitive.NewObjectID()
// Converter string para ObjectID
id, err := primitive.ObjectIDFromHex("507f1f77bcf86cd799439011")
if err != nil {
// Handle error
}
// Converter ObjectID para string
idString := id.Hex()
// Timestamp do ObjectID
timestamp := id.Timestamp()
Operações CRUD
Repository Pattern
// internal/repository/user_repository.go
package repository
import (
"context"
"errors"
"fmt"
"time"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/bson/primitive"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
"project/internal/models"
)
var (
ErrUserNotFound = errors.New("usuário não encontrado")
ErrEmailExists = errors.New("email já existe")
ErrInvalidID = errors.New("ID inválido")
)
// UserRepository interface
type UserRepository interface {
Create(ctx context.Context, user *models.UserCreate) (*models.User, error)
GetByID(ctx context.Context, id string) (*models.User, error)
GetByEmail(ctx context.Context, email string) (*models.User, error)
List(ctx context.Context, filter ListFilter) ([]*models.User, int64, error)
Update(ctx context.Context, id string, update *models.UserUpdate) (*models.User, error)
Delete(ctx context.Context, id string) error
Count(ctx context.Context, filter bson.M) (int64, error)
Exists(ctx context.Context, email string) (bool, error)
}
// userRepo implementação
type userRepo struct {
collection *mongo.Collection
}
// NewUserRepository cria nova instância
func NewUserRepository(db *mongo.Database) UserRepository {
return &userRepo{
collection: db.Collection(models.User{}.CollectionName()),
}
}
CREATE (Inserção)
// Create insere novo usuário
func (r *userRepo) Create(ctx context.Context, user *models.UserCreate) (*models.User, error) {
now := time.Now()
newUser := models.User{
ID: primitive.NewObjectID(),
Email: user.Email,
Name: user.Name,
Password: hashPassword(user.Password),
Profile: user.Profile,
Roles: []string{"user"},
Active: true,
Tags: user.Tags,
CreatedAt: now,
UpdatedAt: now,
}
// Inserir documento
result, err := r.collection.InsertOne(ctx, newUser)
if err != nil {
// Verificar erro de duplicação
if mongo.IsDuplicateKeyError(err) {
return nil, ErrEmailExists
}
return nil, fmt.Errorf("falha ao criar usuário: %w", err)
}
// O InsertedID já é o ID do documento
newUser.ID = result.InsertedID.(primitive.ObjectID)
// Limpar senha antes de retornar
newUser.Password = ""
return &newUser, nil
}
// CreateMany insere múltiplos documentos
func (r *userRepo) CreateMany(ctx context.Context, users []*models.UserCreate) ([]string, error) {
docs := make([]interface{}, len(users))
now := time.Now()
for i, user := range users {
docs[i] = models.User{
ID: primitive.NewObjectID(),
Email: user.Email,
Name: user.Name,
Password: hashPassword(user.Password),
Active: true,
CreatedAt: now,
UpdatedAt: now,
}
}
result, err := r.collection.InsertMany(ctx, docs)
if err != nil {
return nil, fmt.Errorf("falha ao criar usuários: %w", err)
}
// Converter IDs para strings
ids := make([]string, len(result.InsertedIDs))
for i, id := range result.InsertedIDs {
ids[i] = id.(primitive.ObjectID).Hex()
}
return ids, nil
}
func hashPassword(password string) string {
// Use bcrypt em produção!
// import "golang.org/x/crypto/bcrypt"
return "hashed_" + password
}
READ (Consultas)
// GetByID busca por ID
func (r *userRepo) GetByID(ctx context.Context, id string) (*models.User, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, ErrInvalidID
}
var user models.User
err = r.collection.FindOne(ctx, bson.M{"_id": objectID}).Decode(&user)
if err != nil {
if errors.Is(err, mongo.ErrNoDocuments) {
return nil, ErrUserNotFound
}
return nil, fmt.Errorf("falha ao buscar usuário: %w", err)
}
user.Password = "" // Não retornar senha
return &user, nil
}
// GetByEmail busca por email
func (r *userRepo) GetByEmail(ctx context.Context, email string) (*models.User, error) {
var user models.User
err := r.collection.FindOne(ctx, bson.M{"email": email}).Decode(&user)
if err != nil {
if errors.Is(err, mongo.ErrNoDocuments) {
return nil, ErrUserNotFound
}
return nil, fmt.Errorf("falha ao buscar usuário: %w", err)
}
return &user, nil // Retornar com senha para autenticação
}
// ListFilter filtros para listagem
type ListFilter struct {
Active *bool
Roles []string
Tags []string
Search string
SortBy string
SortDesc bool
Page int64
Limit int64
}
// List retorna lista paginada
func (r *userRepo) List(ctx context.Context, filter ListFilter) ([]*models.User, int64, error) {
// Construir filtro
query := bson.M{}
if filter.Active != nil {
query["active"] = *filter.Active
}
if len(filter.Roles) > 0 {
query["roles"] = bson.M{"$in": filter.Roles}
}
if len(filter.Tags) > 0 {
query["tags"] = bson.M{"$in": filter.Tags}
}
if filter.Search != "" {
query["$or"] = []bson.M{
{"name": bson.M{"$regex": filter.Search, "$options": "i"}},
{"email": bson.M{"$regex": filter.Search, "$options": "i"}},
}
}
// Contar total
total, err := r.collection.CountDocuments(ctx, query)
if err != nil {
return nil, 0, fmt.Errorf("falha ao contar documentos: %w", err)
}
// Configurar paginação
if filter.Page < 1 {
filter.Page = 1
}
if filter.Limit < 1 || filter.Limit > 100 {
filter.Limit = 10
}
skip := (filter.Page - 1) * filter.Limit
// Configurar ordenação
sortOrder := 1 // Ascendente
if filter.SortDesc {
sortOrder = -1
}
sortField := filter.SortBy
if sortField == "" {
sortField = "created_at"
}
opts := options.Find().
SetSkip(skip).
SetLimit(filter.Limit).
SetSort(bson.D{{Key: sortField, Value: sortOrder}})
// Executar query
cursor, err := r.collection.Find(ctx, query, opts)
if err != nil {
return nil, 0, fmt.Errorf("falha ao listar usuários: %w", err)
}
defer cursor.Close(ctx)
var users []*models.User
if err := cursor.All(ctx, &users); err != nil {
return nil, 0, fmt.Errorf("falha ao decodificar resultados: %w", err)
}
// Limpar senhas
for _, u := range users {
u.Password = ""
}
return users, total, nil
}
// Exists verifica se email existe
func (r *userRepo) Exists(ctx context.Context, email string) (bool, error) {
count, err := r.collection.CountDocuments(ctx, bson.M{"email": email})
if err != nil {
return false, err
}
return count > 0, nil
}
// Count retorna contagem com filtro
func (r *userRepo) Count(ctx context.Context, filter bson.M) (int64, error) {
return r.collection.CountDocuments(ctx, filter)
}
UPDATE (Atualização)
// Update atualiza usuário parcialmente
func (r *userRepo) Update(ctx context.Context, id string, update *models.UserUpdate) (*models.User, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, ErrInvalidID
}
// Construir update dinâmico
setFields := bson.M{
"updated_at": time.Now(),
}
unsetFields := bson.M{}
if update.Name != nil {
setFields["name"] = *update.Name
}
if update.Email != nil {
setFields["email"] = *update.Email
}
if update.Active != nil {
setFields["active"] = *update.Active
}
if update.Profile != nil {
setFields["profile"] = *update.Profile
}
if update.Roles != nil {
setFields["roles"] = update.Roles
}
if update.Tags != nil {
if len(update.Tags) == 0 {
unsetFields["tags"] = ""
} else {
setFields["tags"] = update.Tags
}
}
updateDoc := bson.M{"$set": setFields}
if len(unsetFields) > 0 {
updateDoc["$unset"] = unsetFields
}
opts := options.FindOneAndUpdate().
SetReturnDocument(options.After)
var user models.User
err = r.collection.FindOneAndUpdate(
ctx,
bson.M{"_id": objectID},
updateDoc,
opts,
).Decode(&user)
if err != nil {
if errors.Is(err, mongo.ErrNoDocuments) {
return nil, ErrUserNotFound
}
if mongo.IsDuplicateKeyError(err) {
return nil, ErrEmailExists
}
return nil, fmt.Errorf("falha ao atualizar usuário: %w", err)
}
user.Password = ""
return &user, nil
}
// UpdateMany atualiza múltiplos documentos
func (r *userRepo) UpdateMany(ctx context.Context, filter bson.M, update bson.M) (int64, error) {
result, err := r.collection.UpdateMany(ctx, filter, update)
if err != nil {
return 0, err
}
return result.ModifiedCount, nil
}
// Replace substitui documento completo
func (r *userRepo) Replace(ctx context.Context, id string, user *models.User) error {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return ErrInvalidID
}
user.UpdatedAt = time.Now()
result, err := r.collection.ReplaceOne(ctx, bson.M{"_id": objectID}, user)
if err != nil {
return err
}
if result.MatchedCount == 0 {
return ErrUserNotFound
}
return nil
}
DELETE (Exclusão)
// Delete remove usuário
func (r *userRepo) Delete(ctx context.Context, id string) error {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return ErrInvalidID
}
result, err := r.collection.DeleteOne(ctx, bson.M{"_id": objectID})
if err != nil {
return fmt.Errorf("falha ao deletar usuário: %w", err)
}
if result.DeletedCount == 0 {
return ErrUserNotFound
}
return nil
}
// SoftDelete (delete lógico)
func (r *userRepo) SoftDelete(ctx context.Context, id string) error {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return ErrInvalidID
}
update := bson.M{
"$set": bson.M{
"active": false,
"deleted_at": time.Now(),
"updated_at": time.Now(),
},
}
result, err := r.collection.UpdateOne(ctx, bson.M{"_id": objectID}, update)
if err != nil {
return err
}
if result.MatchedCount == 0 {
return ErrUserNotFound
}
return nil
}
// DeleteMany remove múltiplos documentos
func (r *userRepo) DeleteMany(ctx context.Context, filter bson.M) (int64, error) {
result, err := r.collection.DeleteMany(ctx, filter)
if err != nil {
return 0, err
}
return result.DeletedCount, nil
}
Aggregation Pipeline
Pipeline Básica
// AggregateUsersStats estatísticas de usuários
func (r *userRepo) AggregateUsersStats(ctx context.Context) (*UserStats, error) {
pipeline := mongo.Pipeline{
// Agrupar por status ativo
{{Key: "$group", Value: bson.M{
"_id": "$active",
"count": bson.M{"$sum": 1},
"avgAge": bson.M{"$avg": "$profile.age"},
}}},
// Ordenar
{{Key: "$sort", Value: bson.M{"_id": 1}}},
}
cursor, err := r.collection.Aggregate(ctx, pipeline)
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
var results []bson.M
if err := cursor.All(ctx, &results); err != nil {
return nil, err
}
// Processar resultados
stats := &UserStats{}
for _, r := range results {
active := r["_id"].(bool)
count := r["count"].(int32)
if active {
stats.ActiveUsers = int(count)
} else {
stats.InactiveUsers = int(count)
}
}
return stats, nil
}
type UserStats struct {
ActiveUsers int `json:"active_users"`
InactiveUsers int `json:"inactive_users"`
}
Pipeline Avançada com Join (Lookup)
// GetUsersWithPosts usuários com seus posts (simulação de JOIN)
func (r *userRepo) GetUsersWithPosts(ctx context.Context, userID string) (*UserWithPosts, error) {
objectID, err := primitive.ObjectIDFromHex(userID)
if err != nil {
return nil, ErrInvalidID
}
pipeline := mongo.Pipeline{
// Match usuário específico
{{Key: "$match", Value: bson.M{"_id": objectID}}},
// Lookup posts do usuário (coleção separada)
{{Key: "$lookup", Value: bson.M{
"from": "posts",
"localField": "_id",
"foreignField": "author_id",
"as": "posts",
}}},
// Adicionar contagem de posts
{{Key: "$addFields", Value: bson.M{
"posts_count": bson.M{"$size": "$posts"},
}}},
// Projetar campos desejados
{{Key: "$project", Value: bson.M{
"password": 0, // Excluir senha
}}},
}
cursor, err := r.collection.Aggregate(ctx, pipeline)
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
var results []UserWithPosts
if err := cursor.All(ctx, &results); err != nil {
return nil, err
}
if len(results) == 0 {
return nil, ErrUserNotFound
}
return &results[0], nil
}
type UserWithPosts struct {
models.User `bson:",inline"`
Posts []Post `bson:"posts" json:"posts"`
PostsCount int `bson:"posts_count" json:"posts_count"`
}
type Post struct {
ID primitive.ObjectID `bson:"_id" json:"id"`
Title string `bson:"title" json:"title"`
Content string `bson:"content" json:"content"`
AuthorID primitive.ObjectID `bson:"author_id" json:"author_id"`
}
Faceted Search
// FacetedSearch busca com facets
func (r *userRepo) FacetedSearch(ctx context.Context, query string) (*FacetedResult, error) {
pipeline := mongo.Pipeline{
// Match na busca
{{Key: "$match", Value: bson.M{
"$or": []bson.M{
{"name": bson.M{"$regex": query, "$options": "i"}},
{"email": bson.M{"$regex": query, "$options": "i"}},
},
}}},
// Facet para múltiplas agregações
{{Key: "$facet", Value: bson.M{
"results": []bson.M{
{"$limit": 20},
{"$project": bson.M{"password": 0}},
},
"totalCount": []bson.M{
{"$count": "count"},
},
"byRole": []bson.M{
{"$unwind": "$roles"},
{"$group": bson.M{
"_id": "$roles",
"count": bson.M{"$sum": 1},
}},
},
"byStatus": []bson.M{
{"$group": bson.M{
"_id": "$active",
"count": bson.M{"$sum": 1},
}},
},
}},
}
cursor, err := r.collection.Aggregate(ctx, pipeline)
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
var results []FacetedResult
if err := cursor.All(ctx, &results); err != nil {
return nil, err
}
if len(results) == 0 {
return &FacetedResult{}, nil
}
return &results[0], nil
}
type FacetedResult struct {
Results []models.User `bson:"results" json:"results"`
TotalCount []struct {
Count int `bson:"count" json:"count"`
} `bson:"totalCount" json:"total_count"`
ByRole []struct {
Role string `bson:"_id" json:"role"`
Count int `bson:"count" json:"count"`
} `bson:"byRole" json:"by_role"`
ByStatus []struct {
Active bool `bson:"_id" json:"active"`
Count int `bson:"count" json:"count"`
} `bson:"byStatus" json:"by_status"`
}
Indexing Strategies
Criar Índices
// CreateIndexes cria índices necessários
func (r *userRepo) CreateIndexes(ctx context.Context) error {
// Índice único para email
emailIndex := mongo.IndexModel{
Keys: bson.D{
{Key: "email", Value: 1},
},
Options: options.Index().SetUnique(true),
}
// Índice composto para busca
nameIndex := mongo.IndexModel{
Keys: bson.D{
{Key: "name", Value: "text"},
{Key: "email", Value: "text"},
},
Options: options.Index().
SetName("text_search").
SetWeights(bson.M{"name": 10, "email": 5}),
}
// Índice para filtro de ativo + data
activeDateIndex := mongo.IndexModel{
Keys: bson.D{
{Key: "active", Value: 1},
{Key: "created_at", Value: -1},
},
}
// Índice TTL para sessões (exemplo)
ttlIndex := mongo.IndexModel{
Keys: bson.D{
{Key: "expires_at", Value: 1},
},
Options: options.Index().
SetExpireAfterSeconds(0).
SetName("session_ttl"),
}
indexes := []mongo.IndexModel{
emailIndex,
nameIndex,
activeDateIndex,
ttlIndex,
}
names, err := r.collection.Indexes().CreateMany(ctx, indexes)
if err != nil {
return fmt.Errorf("falha ao criar índices: %w", err)
}
log.Printf("✅ Índices criados: %v", names)
return nil
}
Gerenciar Índices
// ListIndexes lista todos os índices
func (r *userRepo) ListIndexes(ctx context.Context) ([]bson.M, error) {
cursor, err := r.collection.Indexes().List(ctx)
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
var indexes []bson.M
if err := cursor.All(ctx, &indexes); err != nil {
return nil, err
}
return indexes, nil
}
// DropIndex remove um índice
func (r *userRepo) DropIndex(ctx context.Context, name string) error {
_, err := r.collection.Indexes().DropOne(ctx, name)
return err
}
Transações
Transações Multi-Documento
// TransferSubscription transação com múltiplas operações
func (r *userRepo) TransferSubscription(
ctx context.Context,
fromUserID, toUserID string,
plan string,
) error {
// Converter IDs
fromID, err := primitive.ObjectIDFromHex(fromUserID)
if err != nil {
return ErrInvalidID
}
toID, err := primitive.ObjectIDFromHex(toUserID)
if err != nil {
return ErrInvalidID
}
// Iniciar sessão para transação
session, err := r.collection.Database().Client().StartSession()
if err != nil {
return fmt.Errorf("falha ao iniciar sessão: %w", err)
}
defer session.EndSession(ctx)
// Callback da transação
callback := func(sessCtx mongo.SessionContext) (interface{}, error) {
// 1. Remover plano do usuário origem
_, err := r.collection.UpdateOne(
sessCtx,
bson.M{"_id": fromID},
bson.M{
"$pull": bson.M{"subscriptions": bson.M{"plan": plan}},
"$set": bson.M{"updated_at": time.Now()},
},
)
if err != nil {
return nil, fmt.Errorf("falha ao remover do origem: %w", err)
}
// 2. Adicionar plano ao usuário destino
_, err = r.collection.UpdateOne(
sessCtx,
bson.M{"_id": toID},
bson.M{
"$push": bson.M{"subscriptions": bson.M{
"plan": plan,
"transferred_at": time.Now(),
}},
"$set": bson.M{"updated_at": time.Now()},
},
)
if err != nil {
return nil, fmt.Errorf("falha ao adicionar ao destino: %w", err)
}
// 3. Registrar transferência (outra coleção)
transfers := r.collection.Database().Collection("transfers")
_, err = transfers.InsertOne(sessCtx, bson.M{
"from_user_id": fromID,
"to_user_id": toID,
"plan": plan,
"created_at": time.Now(),
})
if err != nil {
return nil, fmt.Errorf("falha ao registrar transferência: %w", err)
}
return nil, nil
}
// Executar transação
_, err = session.WithTransaction(ctx, callback)
if err != nil {
return fmt.Errorf("transação falhou: %w", err)
}
return nil
}
Boas Práticas
1. Context com Timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
user, err := repo.GetByID(ctx, id)
2. Connection Pool
clientOptions := options.Client().
SetMaxPoolSize(100).
SetMinPoolSize(10).
SetMaxConnIdleTime(30 * time.Second)
3. Tratamento de Erros
func mapMongoError(err error) error {
if err == nil {
return nil
}
if errors.Is(err, mongo.ErrNoDocuments) {
return ErrNotFound
}
if mongo.IsDuplicateKeyError(err) {
return ErrDuplicate
}
if mongo.IsTimeout(err) {
return ErrTimeout
}
return err
}
4. Paginação Eficiente
// Use cursor-based pagination para grandes datasets
func (r *userRepo) ListWithCursor(ctx context.Context, lastID string, limit int) ([]*models.User, string, error) {
filter := bson.M{}
if lastID != "" {
id, _ := primitive.ObjectIDFromHex(lastID)
filter["_id"] = bson.M{"$gt": id}
}
opts := options.Find().
SetLimit(int64(limit + 1)). // Buscar um extra para saber se há mais
SetSort(bson.M{"_id": 1})
cursor, err := r.collection.Find(ctx, filter, opts)
if err != nil {
return nil, "", err
}
defer cursor.Close(ctx)
var users []*models.User
if err := cursor.All(ctx, &users); err != nil {
return nil, "", err
}
var nextCursor string
if len(users) > limit {
nextCursor = users[limit].ID.Hex()
users = users[:limit]
}
return users, nextCursor, nil
}
5. Projeções para Performance
// Retornar apenas campos necessários
opts := options.Find().SetProjection(bson.M{
"password": 0, // Excluir
"metadata": 0, // Excluir
"name": 1, // Incluir
"email": 1, // Incluir
})
Checklist para Produção
- Índices criados para queries frequentes
- Connection pool configurado adequadamente
- Timeouts em todas as operações
- Transações para operações multi-documento
- Projeções para limitar dados transferidos
- Paginação para grandes datasets
- Replica Set configurado (não standalone)
- Autenticação habilitada
- TLS/SSL para conexões
- Backup automatizado
Próximos Passos
Aprofunde seus conhecimentos:
Go e PostgreSQL: CRUD Completo com Boas Práticas
Aprenda a integrar Go com PostgreSQL de forma profissional. Domine CRUD operations, transações, migrations, query builders e a ferramenta sqlc para type-safe SQL.
PostgreSQL é um dos bancos de dados relacionais mais robustos e populares do mundo. Combinado com Go, você tem uma stack poderosa para aplicações de alta performance. Neste guia, você aprenderá todas as técnicas essenciais para trabalhar com PostgreSQL em Go, desde operações básicas até padrões avançados de produção.
Por Que PostgreSQL com Go?
O Match Perfeito
┌─────────────────────────────────────────────────────────────────┐
│ POSTGRESQL │ GO │
├─────────────────────────────────────────────────────────────────┤
│ Confiável │ Type-safe, compilação rigorosa │
├─────────────────────────────────────────────────────────────────┤
│ ACID transactions │ Error handling explícito │
├─────────────────────────────────────────────────────────────────┤
│ Alto desempenho │ Runtime eficiente, baixa latência │
├─────────────────────────────────────────────────────────────────┤
│ Extensível │ Interfaces flexíveis │
├─────────────────────────────────────────────────────────────────┤
│ Open source │ Open source │
└─────────────────────────────────────────────────────────────────┘
Configuração do Projeto
Instalação das Dependências
# Driver padrão PostgreSQL
go get -u github.com/lib/pq
# Driver alternativo (mais performático)
go get -u github.com/jackc/pgx/v5
# sqlc - type-safe SQL
go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
# Migration tool
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
Estrutura do Projeto
project/
├── cmd/
│ └── api/
│ └── main.go
├── internal/
│ ├── config/
│ │ └── database.go
│ ├── models/
│ │ └── user.go
│ ├── repository/
│ │ └── user_repository.go
│ └── service/
│ └── user_service.go
├── migrations/
│ ├── 001_create_users_table.up.sql
│ ├── 001_create_users_table.down.sql
│ └── ...
├── queries/
│ └── user.sql
├── sqlc.yaml
├── go.mod
└── .env
Conexão com PostgreSQL
Usando database/sql (Padrão)
// internal/config/database.go
package config
import (
"database/sql"
"fmt"
"log"
"os"
"time"
_ "github.com/lib/pq"
)
// Configurações do banco
type DBConfig struct {
Host string
Port int
User string
Password string
Database string
SSLMode string
}
func NewDBConfig() *DBConfig {
return &DBConfig{
Host: getEnv("DB_HOST", "localhost"),
Port: getEnvAsInt("DB_PORT", 5432),
User: getEnv("DB_USER", "postgres"),
Password: getEnv("DB_PASSWORD", "password"),
Database: getEnv("DB_NAME", "myapp"),
SSLMode: getEnv("DB_SSLMODE", "disable"),
}
}
func (c *DBConfig) ConnectionString() string {
return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
c.Host, c.Port, c.User, c.Password, c.Database, c.SSLMode)
}
// Conexão com pool
func NewDB(cfg *DBConfig) (*sql.DB, error) {
db, err := sql.Open("postgres", cfg.ConnectionString())
if err != nil {
return nil, fmt.Errorf("falha ao abrir conexão: %w", err)
}
// Configurar pool de conexões
db.SetMaxOpenConns(25) // Máximo de conexões abertas
db.SetMaxIdleConns(10) // Máximo de conexões idle
db.SetConnMaxLifetime(time.Hour) // Tempo máximo de vida
db.SetConnMaxIdleTime(30 * time.Minute)
// Verificar conexão
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
return nil, fmt.Errorf("falha ao ping banco de dados: %w", err)
}
log.Println("✅ Conectado ao PostgreSQL")
return db, nil
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func getEnvAsInt(key string, defaultValue int) int {
if value := os.Getenv(key); value != "" {
var intValue int
fmt.Sscanf(value, "%d", &intValue)
return intValue
}
return defaultValue
}
Usando pgx (Recomendado)
// internal/config/pgx_database.go
package config
import (
"context"
"fmt"
"log"
"github.com/jackc/pgx/v5/pgxpool"
)
func NewPgxPool(cfg *DBConfig) (*pgxpool.Pool, error) {
connString := fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&pool_max_conns=25",
cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Database, cfg.SSLMode)
pool, err := pgxpool.New(context.Background(), connString)
if err != nil {
return nil, fmt.Errorf("falha ao criar pool: %w", err)
}
// Verificar conexão
if err := pool.Ping(context.Background()); err != nil {
return nil, fmt.Errorf("falha ao ping: %w", err)
}
log.Println("✅ Conectado ao PostgreSQL com pgx")
return pool, nil
}
Modelos e Estruturas
// internal/models/user.go
package models
import (
"time"
)
// User representa um usuário no banco
type User struct {
ID int64 `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
Password string `json:"-"` // Não serializar
Active bool `json:"active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// UserCreate é usado para criar usuários
type UserCreate struct {
Email string `json:"email" validate:"required,email"`
Name string `json:"name" validate:"required,min=3,max=100"`
Password string `json:"password" validate:"required,min=8"`
}
// UserUpdate é usado para atualizar usuários
type UserUpdate struct {
Email *string `json:"email,omitempty"`
Name *string `json:"name,omitempty"`
Active *bool `json:"active,omitempty"`
}
// TableName retorna o nome da tabela
func (User) TableName() string {
return "users"
}
Operações CRUD
Repository Pattern
// internal/repository/user_repository.go
package repository
import (
"context"
"database/sql"
"errors"
"fmt"
"project/internal/models"
)
var (
ErrUserNotFound = errors.New("usuário não encontrado")
ErrEmailExists = errors.New("email já existe")
)
// UserRepository define operações de banco
type UserRepository interface {
Create(ctx context.Context, user *models.UserCreate) (*models.User, error)
GetByID(ctx context.Context, id int64) (*models.User, error)
GetByEmail(ctx context.Context, email string) (*models.User, error)
List(ctx context.Context, limit, offset int) ([]*models.User, error)
Update(ctx context.Context, id int64, update *models.UserUpdate) (*models.User, error)
Delete(ctx context.Context, id int64) error
Count(ctx context.Context) (int64, error)
}
// userRepo implementa UserRepository
type userRepo struct {
db *sql.DB
}
// NewUserRepository cria nova instância
func NewUserRepository(db *sql.DB) UserRepository {
return &userRepo{db: db}
}
CREATE
// Create insere novo usuário
func (r *userRepo) Create(ctx context.Context, user *models.UserCreate) (*models.User, error) {
query := `
INSERT INTO users (email, name, password, active, created_at, updated_at)
VALUES ($1, $2, $3, $4, NOW(), NOW())
RETURNING id, email, name, active, created_at, updated_at
`
// Em produção, faça hash da senha!
hashedPassword := hashPassword(user.Password)
var created models.User
err := r.db.QueryRowContext(ctx, query,
user.Email,
user.Name,
hashedPassword,
true,
).Scan(
&created.ID,
&created.Email,
&created.Name,
&created.Active,
&created.CreatedAt,
&created.UpdatedAt,
)
if err != nil {
// Verificar erro de duplicação
if isDuplicateKeyError(err) {
return nil, ErrEmailExists
}
return nil, fmt.Errorf("falha ao criar usuário: %w", err)
}
return &created, nil
}
func hashPassword(password string) string {
// Use bcrypt em produção!
// import "golang.org/x/crypto/bcrypt"
// hashed, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
// return string(hashed)
return "hashed_" + password
}
func isDuplicateKeyError(err error) bool {
if err == nil {
return false
}
// Verificar código de erro PostgreSQL para duplicação
return strings.Contains(err.Error(), "duplicate key")
}
READ
// GetByID busca usuário por ID
func (r *userRepo) GetByID(ctx context.Context, id int64) (*models.User, error) {
query := `
SELECT id, email, name, active, created_at, updated_at
FROM users
WHERE id = $1
`
var user models.User
err := r.db.QueryRowContext(ctx, query, id).Scan(
&user.ID,
&user.Email,
&user.Name,
&user.Active,
&user.CreatedAt,
&user.UpdatedAt,
)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrUserNotFound
}
return nil, fmt.Errorf("falha ao buscar usuário: %w", err)
}
return &user, nil
}
// GetByEmail busca usuário por email
func (r *userRepo) GetByEmail(ctx context.Context, email string) (*models.User, error) {
query := `
SELECT id, email, name, active, created_at, updated_at
FROM users
WHERE email = $1
`
var user models.User
err := r.db.QueryRowContext(ctx, query, email).Scan(
&user.ID,
&user.Email,
&user.Name,
&user.Active,
&user.CreatedAt,
&user.UpdatedAt,
)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrUserNotFound
}
return nil, fmt.Errorf("falha ao buscar usuário: %w", err)
}
return &user, nil
}
// List retorna lista paginada de usuários
func (r *userRepo) List(ctx context.Context, limit, offset int) ([]*models.User, error) {
query := `
SELECT id, email, name, active, created_at, updated_at
FROM users
ORDER BY created_at DESC
LIMIT $1 OFFSET $2
`
if limit <= 0 {
limit = 10
}
if limit > 100 {
limit = 100 // Máximo por página
}
rows, err := r.db.QueryContext(ctx, query, limit, offset)
if err != nil {
return nil, fmt.Errorf("falha ao listar usuários: %w", err)
}
defer rows.Close()
var users []*models.User
for rows.Next() {
var user models.User
err := rows.Scan(
&user.ID,
&user.Email,
&user.Name,
&user.Active,
&user.CreatedAt,
&user.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("falha ao scan usuário: %w", err)
}
users = append(users, &user)
}
if err = rows.Err(); err != nil {
return nil, fmt.Errorf("erro ao iterar rows: %w", err)
}
return users, nil
}
// Count retorna total de usuários
func (r *userRepo) Count(ctx context.Context) (int64, error) {
query := `SELECT COUNT(*) FROM users`
var count int64
err := r.db.QueryRowContext(ctx, query).Scan(&count)
if err != nil {
return 0, fmt.Errorf("falha ao contar usuários: %w", err)
}
return count, nil
}
UPDATE
// Update atualiza usuário
func (r *userRepo) Update(ctx context.Context, id int64, update *models.UserUpdate) (*models.User, error) {
// Construir query dinamicamente
var setParts []string
var args []interface{}
argIndex := 1
if update.Email != nil {
setParts = append(setParts, fmt.Sprintf("email = $%d", argIndex))
args = append(args, *update.Email)
argIndex++
}
if update.Name != nil {
setParts = append(setParts, fmt.Sprintf("name = $%d", argIndex))
args = append(args, *update.Name)
argIndex++
}
if update.Active != nil {
setParts = append(setParts, fmt.Sprintf("active = $%d", argIndex))
args = append(args, *update.Active)
argIndex++
}
if len(setParts) == 0 {
return nil, errors.New("nenhum campo para atualizar")
}
// Sempre atualizar updated_at
setParts = append(setParts, fmt.Sprintf("updated_at = $%d", argIndex))
args = append(args, time.Now())
argIndex++
// Adicionar ID
args = append(args, id)
query := fmt.Sprintf(`
UPDATE users
SET %s
WHERE id = $%d
RETURNING id, email, name, active, created_at, updated_at
`, strings.Join(setParts, ", "), argIndex)
var user models.User
err := r.db.QueryRowContext(ctx, query, args...).Scan(
&user.ID,
&user.Email,
&user.Name,
&user.Active,
&user.CreatedAt,
&user.UpdatedAt,
)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrUserNotFound
}
if isDuplicateKeyError(err) {
return nil, ErrEmailExists
}
return nil, fmt.Errorf("falha ao atualizar usuário: %w", err)
}
return &user, nil
}
DELETE
// Delete remove usuário
func (r *userRepo) Delete(ctx context.Context, id int64) error {
query := `DELETE FROM users WHERE id = $1`
result, err := r.db.ExecContext(ctx, query, id)
if err != nil {
return fmt.Errorf("falha ao deletar usuário: %w", err)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("falha ao obter rows afetadas: %w", err)
}
if rowsAffected == 0 {
return ErrUserNotFound
}
return nil
}
// SoftDelete (alternativa ao delete físico)
func (r *userRepo) SoftDelete(ctx context.Context, id int64) error {
query := `
UPDATE users
SET active = false, deleted_at = NOW()
WHERE id = $1
`
result, err := r.db.ExecContext(ctx, query, id)
if err != nil {
return fmt.Errorf("falha ao deletar usuário: %w", err)
}
rowsAffected, _ := result.RowsAffected()
if rowsAffected == 0 {
return ErrUserNotFound
}
return nil
}
Transações
// WithTransaction executa função dentro de transação
func (r *userRepo) WithTransaction(ctx context.Context, fn func(*sql.Tx) error) error {
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("falha ao iniciar transação: %w", err)
}
defer func() {
if p := recover(); p != nil {
tx.Rollback()
panic(p)
}
}()
if err := fn(tx); err != nil {
tx.Rollback()
return err
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("falha ao commit: %w", err)
}
return nil
}
// TransferBalance exemplo de transação
func TransferBalance(ctx context.Context, db *sql.DB, fromID, toID int64, amount float64) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
// Verificar saldo
var balance float64
err = tx.QueryRowContext(ctx,
"SELECT balance FROM accounts WHERE id = $1 FOR UPDATE",
fromID,
).Scan(&balance)
if err != nil {
return err
}
if balance < amount {
return errors.New("saldo insuficiente")
}
// Debitar
_, err = tx.ExecContext(ctx,
"UPDATE accounts SET balance = balance - $1 WHERE id = $2",
amount, fromID,
)
if err != nil {
return err
}
// Creditar
_, err = tx.ExecContext(ctx,
"UPDATE accounts SET balance = balance + $1 WHERE id = $2",
amount, toID,
)
if err != nil {
return err
}
// Registrar transação
_, err = tx.ExecContext(ctx,
`INSERT INTO transactions (from_id, to_id, amount, created_at)
VALUES ($1, $2, $3, NOW())`,
fromID, toID, amount,
)
if err != nil {
return err
}
return tx.Commit()
}
sqlc: Type-Safe SQL
Configuração
# sqlc.yaml
version: "2"
sql:
- schema: "migrations/"
queries: "queries/"
engine: "postgresql"
gen:
go:
package: "db"
out: "internal/db"
sql_package: "pgx/v5"
emit_json_tags: true
emit_prepared_queries: true
emit_interface: true
emit_empty_slices: true
Queries SQL
-- queries/user.sql
-- name: CreateUser :one
INSERT INTO users (email, name, password, active)
VALUES ($1, $2, $3, $4)
RETURNING *;
-- name: GetUser :one
SELECT * FROM users
WHERE id = $1;
-- name: GetUserByEmail :one
SELECT * FROM users
WHERE email = $1;
-- name: ListUsers :many
SELECT * FROM users
ORDER BY created_at DESC
LIMIT $1 OFFSET $2;
-- name: UpdateUser :one
UPDATE users
SET email = COALESCE($2, email),
name = COALESCE($3, name),
active = COALESCE($4, active),
updated_at = NOW()
WHERE id = $1
RETURNING *;
-- name: DeleteUser :exec
DELETE FROM users WHERE id = $1;
-- name: CountUsers :one
SELECT COUNT(*) FROM users;
Gerar Código
# Gerar código Go a partir das queries
sqlc generate
# Estrutura gerada:
# internal/db/
# ├── db.go
# ├── models.go
# ├── querier.go (interface)
# └── user.sql.go
Uso do sqlc
// internal/repository/user_sqlc.go
package repository
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
"project/internal/db"
)
type UserSQLCRepository struct {
queries *db.Queries
}
func NewUserSQLCRepository(queries *db.Queries) *UserSQLCRepository {
return &UserSQLCRepository{queries: queries}
}
func (r *UserSQLCRepository) Create(ctx context.Context, email, name, password string) (*db.User, error) {
user, err := r.queries.CreateUser(ctx, db.CreateUserParams{
Email: email,
Name: name,
Password: password,
Active: true,
})
if err != nil {
return nil, err
}
return &user, nil
}
func (r *UserSQLCRepository) List(ctx context.Context, limit, offset int32) ([]db.User, error) {
return r.queries.ListUsers(ctx, db.ListUsersParams{
Limit: limit,
Offset: offset,
})
}
func (r *UserSQLCRepository) Update(ctx context.Context, id int64, email, name *string, active *bool) (*db.User, error) {
var emailParam, nameParam pgtype.Text
var activeParam pgtype.Bool
if email != nil {
emailParam = pgtype.Text{String: *email, Valid: true}
}
if name != nil {
nameParam = pgtype.Text{String: *name, Valid: true}
}
if active != nil {
activeParam = pgtype.Bool{Bool: *active, Valid: true}
}
user, err := r.queries.UpdateUser(ctx, db.UpdateUserParams{
ID: id,
Email: emailParam,
Name: nameParam,
Active: activeParam,
})
if err != nil {
return nil, err
}
return &user, nil
}
Migrations
Usando golang-migrate
# Criar nova migration
migrate create -ext sql -dir migrations -seq create_users_table
# Aplicar migrations
migrate -path migrations -database "postgres://user:pass@localhost/db?sslmode=disable" up
# Reverter última migration
migrate -path migrations -database "postgres://user:pass@localhost/db?sslmode=disable" down 1
Migration Files
-- migrations/001_create_users_table.up.sql
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
password VARCHAR(255) NOT NULL,
active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_active ON users(active);
-- Trigger para atualizar updated_at
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- migrations/001_create_users_table.down.sql
DROP TRIGGER IF EXISTS update_users_updated_at ON users;
DROP FUNCTION IF EXISTS update_updated_at_column();
DROP INDEX IF EXISTS idx_users_active;
DROP INDEX IF EXISTS idx_users_email;
DROP TABLE IF EXISTS users;
Boas Práticas
1. Context para Timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
user, err := repo.GetByID(ctx, id)
2. Prepared Statements
// database/sql já usa prepared statements internamente
// Para queries repetidas, use Prepare
stmt, err := db.PrepareContext(ctx, "SELECT * FROM users WHERE id = $1")
if err != nil {
return err
}
defer stmt.Close()
for _, id := range ids {
row := stmt.QueryRowContext(ctx, id)
// ...
}
3. Connection Pool
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(time.Hour)
4. Error Handling
var (
ErrNotFound = errors.New("recurso não encontrado")
ErrDuplicate = errors.New("recurso já existe")
ErrConstraint = errors.New("violação de constraint")
)
func mapError(err error) error {
if err == nil {
return nil
}
if errors.Is(err, sql.ErrNoRows) {
return ErrNotFound
}
pqErr, ok := err.(*pq.Error)
if ok {
switch pqErr.Code {
case "23505": // unique_violation
return ErrDuplicate
case "23503": // foreign_key_violation
return ErrConstraint
}
}
return err
}
5. Logging
// Log queries em desenvolvimento
func logQuery(query string, args ...interface{}) {
if os.Getenv("ENV") == "development" {
log.Printf("[SQL] %s | Args: %v", query, args)
}
}
Checklist para Produção
- Connection pool configurado corretamente
- Timeouts em todas as queries (context)
- Migrations automatizadas no deploy
- Prepared statements para queries frequentes
- Índices otimizados
- Hash de senhas (bcrypt/argon2)
- Soft delete para dados importantes
- Auditoria (created_at, updated_at)
- SSL para conexões (sslmode=require)
- Backup automatizado
Próximos Passos
Aprofunde seus conhecimentos:
Go Observability: Logs, Métricas e Traces - Guia Completo
Aprenda observabilidade em Go com logging estruturado, métricas Prometheus, distributed tracing e OpenTelemetry. Tutorial completo para monitorar aplicações Go em produção.
Observabilidade é essencial para aplicações em produção. Este guia completo mostra como implementar logging estruturado, métricas, traces e alerting em Go usando as melhores práticas e ferramentas modernas.
Pilares da Observabilidade
┌─────────────────────────────────────────────────────────┐
│ OBSERVABILITY │
├─────────────┬─────────────┬─────────────────────────────┤
│ LOGS │ METRICS │ TRACES │
│ │ │ │
│ O que │ Quanto/ │ Onde/O │
│ aconteceu │ Quando │ quando │
│ │ │ │
│ Eventos │ Números │ Request flow │
│ detalhados │ agregados │ distribuído │
└─────────────┴─────────────┴─────────────────────────────┘
1. Structured Logging
Logging estruturado é fundamental para debug e análise de incidentes.
Go Performance: Profiling e Otimização de Alto Desempenho
Domine profiling e otimização em Go. Aprenda a usar pprof para CPU e memory profiling, escrever benchmarks com testing.B, identificar gargalos e aplicar estratégias avançadas de otimização. Guia completo com casos reais.
Performance é diferencial competitivo. Enquanto concorrentes lutam com latência, aplicações Go otimizadas processam milhões de requisições por segundo. Neste guia completo, você vai dominar as ferramentas e técnicas usadas por engenheiros da Google, Netflix e Uber para criar sistemas de alto desempenho.
Por Que Performance Importa em Go?
O Custo da Lentidão
| Métrica | Impacto no Negócio |
|---|---|
| +100ms de latência | -1% de conversão (Amazon) |
| +500ms de latência | -20% de tráfego (Google) |
| +1s de latência | -11% pageviews (Bing) |
| 3s de carga | 53% abandonam mobile |
Em Go, cada microssegundo conta. Um serviço que processa 10M requests/dia economiza 2.7 horas de CPU por dia com apenas 1ms de otimização por request.
Go Testing: TDD e Integração Contínua - Guia Completo
Aprenda Test-Driven Development (TDD) e CI/CD para Go. Tutorial completo com GitHub Actions, testes automatizados, pipelines e deploy contínuo para projetos Go profissionais.
Test-Driven Development (TDD) e Integração Contínua (CI/CD) são práticas essenciais para desenvolvimento de software profissional. Este guia completo mostra como implementar TDD e pipelines de CI/CD em projetos Go, desde testes unitários até deploy automatizado.
Por Que TDD + CI/CD em Go?
Go é ideal para TDD e CI/CD porque:
- Testes rápidos — Compilação e execução em segundos
- Binário único — Deploy simplificado
- Biblioteca padrão robusta — Sem dependências complexas
- Cross-compilation nativa — Build para múltiplas plataformas
Fundamentos de TDD
O Ciclo Red-Green-Refactor
┌─────────┐ ┌─────────┐ ┌─────────┐
│ RED │ → │ GREEN │ → │ REFACTOR│
│ (Falha)│ │ (Passa) │ │(Melhora)│
└─────────┘ └─────────┘ └─────────┘
↑ │
└──────────────────────────────┘
Exemplo Prático: Calculadora com TDD
Passo 1: Escreva o teste (RED)
Go e Redis: Cache e Session Store Completo
Aprenda a usar Redis com Go para caching de alta performance, gerenciamento de sessões e arquiteturas escaláveis. Guia completo com exemplos práticos.
Go e Redis: Cache e Session Store Completo
O Redis é um dos bancos de dados em memória mais populares do mundo, e quando combinado com Go, cria aplicações extremamente rápidas e escaláveis. Neste guia completo, você vai aprender a implementar caching de alta performance e gerenciamento de sessões usando Go e Redis.
Por Que Usar Redis com Go?
Antes de mergulhar no código, entenda por que essa combinação é tão poderosa:
Go Clean Architecture: Organizando Projetos Escaláveis
Domine a Clean Architecture em Go. Aprenda a organizar projetos com ports e adapters, dependency injection, repository pattern e testes eficientes. Estrutura profissional para aplicações Go.
Clean Architecture (Arquitetura Limpa) é o padrão usado por empresas como Netflix, Uber e Google para construir sistemas escaláveis e testáveis. Em Go, ela brilha pela simplicidade que combina com a filosofia da linguagem. Neste guia, você vai aprender a organizar projetos Go profissionais.
Por Que Clean Architecture em Go?
Os Problemas Sem Arquitetura
📁 projeto-caotico/
├── main.go # 2000 linhas
├── handlers.go # Tudo misturado
├── database.go # SQL espalhado
├── models.go # Regras de negócio aqui
└── utils.go # Deus sabe o que tem aqui
Problemas:
Go Concurrency Patterns: Goroutines e Channels Avançados
Aprenda patterns avançados de concorrência em Go: worker pools, fan-out/fan-in, rate limiting com channels, e context package. Tutorial completo com exemplos práticos de código.
A concorrência é um dos recursos mais poderosos de Go. Enquanto muitas linguagens tratam concorrência como um recurso avançado complexo, Go a torna acessível através de goroutines e channels. Este tutorial explora patterns avançados que você encontrará em aplicações Go de produção.
Pré-requisitos: Conhecimento básico de Go (variáveis, funções, structs) e familiaridade com goroutines e channels. Se você é novo em Go, confira nosso guia para iniciantes.
Sumário dos Patterns
| Pattern | Use Case | Complexidade |
|---|---|---|
| Worker Pool | Processar tarefas em paralelo com controle | ⭐⭐ |
| Fan-Out/Fan-In | Distribuir trabalho e agregar resultados | ⭐⭐⭐ |
| Pipeline | Processar dados em estágios sequenciais | ⭐⭐ |
| Rate Limiting | Controlar throughput de requisições | ⭐⭐⭐ |
| Context Cancellation | Cancelar operações de forma segura | ⭐⭐ |
| Select Statement | Multiplexar channels | ⭐⭐⭐ |
1. Worker Pool Pattern
O pattern Worker Pool é essencial quando você precisa processar um grande número de tarefas de forma concorrente, mas quer limitar o número de goroutines simultâneas.
Go para APIs REST: Guia Completo de Desenvolvimento Web
Aprenda a criar APIs REST robustas em Go. Tutorial completo cobrindo rotas, JSON, middleware, autenticação, banco de dados e deploy. Exemplos práticos incluídos.
Go é uma das melhores linguagens para desenvolvimento de APIs REST. Com sua performance nativa, concorrência eficiente via goroutines e biblioteca padrão robusta, Go permite criar serviços web escaláveis e de alta performance. Neste guia completo, vamos construir uma API REST do zero, cobrindo desde conceitos básicos até padrões avançados usados em produção.
Por Que Go para APIs REST?
Vantagens de Go para Backend
┌─────────────────────────────────────────────────────────┐
│ PERFORMANCE │ Goroutines leves (2KB stack) │
├─────────────────────────────────────────────────────────┤
│ CONFIABILIDADE │ Tipagem forte, erro explícito │
├─────────────────────────────────────────────────────────┤
│ PRODUTIVIDADE │ Compilação rápida, stdlib rica │
├─────────────────────────────────────────────────────────┤
│ DEPLOY │ Binário único, cross-compile │
├─────────────────────────────────────────────────────────┤
│ ECOSISTEMA │ Gin, Echo, Chi, Fiber │
└─────────────────────────────────────────────────────────┘
Empresas usando Go em produção:
Go para Microserviços: Arquitetura e Práticas de Produção
Domine a arquitetura de microserviços em Go. Aprenda padrões de comunicação, service discovery, circuit breakers, distributed tracing e estratégias de deploy com exemplos práticos.
Microserviços transformaram a forma como desenvolvemos sistemas escaláveis. Go é a linguagem preferida para microserviços em empresas como Netflix, Uber, e Kubernetes. Neste guia completo, você aprenderá a arquitetar, construir e operar microserviços robustos em Go.
Por Que Go é Perfeito para Microserviços
O Match Perfeito
┌─────────────────────────────────────────────────────────────────┐
│ MICROSERVIÇO │ GO │
├─────────────────────────────────────────────────────────────────┤
│ Leve e rápido │ Binário único, startup < 100ms │
├─────────────────────────────────────────────────────────────────┤
│ Escalável │ Goroutines (milhões simultâneas) │
├─────────────────────────────────────────────────────────────────┤
│ Confiável │ Tipagem forte, sem runtime pesado │
├─────────────────────────────────────────────────────────────────┤
│ Eficiente │ Memória mínima (10-50MB por serviço) │
├─────────────────────────────────────────────────────────────────┤
│ Portátil │ Cross-compile, Docker nativo │
├─────────────────────────────────────────────────────────────────┤
│ Simples │ Código explícito, fácil manutenção │
└─────────────────────────────────────────────────────────────────┘
Comparação com Outras Linguagens
| Aspecto | Go | Java | Node.js | Python |
|---|---|---|---|---|
| Startup | ~50ms | ~5s | ~2s | ~1s |
| Memória | 10-50MB | 200-500MB | 100-300MB | 150-400MB |
| Concorrência | Nativa | Threads pesadas | Callbacks | GIL limita |
| Binário | Único | JVM necessária | Node runtime | Interpreter |
| Performance | Nativa | Boa (JIT) | Média | Lenta |
Arquitetura de um Microserviço Go
Estrutura de Projeto Recomendada
order-service/
├── cmd/
│ └── api/
│ └── main.go # Entry point
├── internal/
│ ├── domain/
│ │ ├── order.go # Entidades
│ │ └── errors.go # Erros de domínio
│ ├── application/
│ │ ├── service.go # Lógica de negócio
│ │ └── dto.go # Data Transfer Objects
│ ├── infrastructure/
│ │ ├── http/
│ │ │ ├── handler.go # HTTP handlers
│ │ │ ├── router.go # Configuração de rotas
│ │ │ └── middleware.go # Middlewares
│ │ ├── persistence/
│ │ │ ├── postgres.go # Implementação do repo
│ │ │ └── redis.go # Cache
│ │ └── messaging/
│ │ └── kafka.go # Event publisher
│ └── config/
│ └── config.go # Configurações
├── pkg/
│ └── logger/ # Shared packages
├── api/
│ └── proto/ # Protocol Buffers
├── deployments/
│ ├── docker/
│ └── k8s/
├── go.mod
└── Makefile
Implementação Completa
// internal/domain/order.go
package domain
import (
"errors"
"time"
)
var (
ErrInvalidAmount = errors.New("valor do pedido inválido")
ErrOrderNotFound = errors.New("pedido não encontrado")
)
type Order struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Items []Item `json:"items"`
Total float64 `json:"total"`
Status Status `json:"status"`
CreatedAt time.Time `json:"created_at"`
}
type Item struct {
ProductID string `json:"product_id"`
Quantity int `json:"quantity"`
Price float64 `json:"price"`
}
type Status string
const (
StatusPending Status = "PENDING"
StatusPaid Status = "PAID"
StatusShipped Status = "SHIPPED"
StatusDelivered Status = "DELIVERED"
)
func (o *Order) CalculateTotal() {
var total float64
for _, item := range o.Items {
total += item.Price * float64(item.Quantity)
}
o.Total = total
}
func (o *Order) Validate() error {
if o.Total <= 0 {
return ErrInvalidAmount
}
return nil
}
// internal/application/service.go
package application
import (
"context"
"fmt"
"time"
"order-service/internal/domain"
)
// Portas (interfaces) para dependências
type OrderRepository interface {
Save(ctx context.Context, order *domain.Order) error
GetByID(ctx context.Context, id string) (*domain.Order, error)
Update(ctx context.Context, order *domain.Order) error
}
type PaymentService interface {
Process(ctx context.Context, orderID string, amount float64) error
}
type EventPublisher interface {
PublishOrderCreated(ctx context.Context, order *domain.Order) error
}
type OrderService struct {
repo OrderRepository
payment PaymentService
publisher EventPublisher
}
func NewOrderService(
repo OrderRepository,
payment PaymentService,
publisher EventPublisher,
) *OrderService {
return &OrderService{
repo: repo,
payment: payment,
publisher: publisher,
}
}
func (s *OrderService) CreateOrder(ctx context.Context, userID string, items []domain.Item) (*domain.Order, error) {
order := &domain.Order{
ID: generateID(),
UserID: userID,
Items: items,
Status: domain.StatusPending,
CreatedAt: time.Now(),
}
order.CalculateTotal()
if err := order.Validate(); err != nil {
return nil, err
}
// Salvar no banco
if err := s.repo.Save(ctx, order); err != nil {
return nil, fmt.Errorf("falha ao salvar pedido: %w", err)
}
// Publicar evento assíncrono
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
s.publisher.PublishOrderCreated(ctx, order)
}()
return order, nil
}
func (s *OrderService) ProcessPayment(ctx context.Context, orderID string) error {
order, err := s.repo.GetByID(ctx, orderID)
if err != nil {
return err
}
if err := s.payment.Process(ctx, orderID, order.Total); err != nil {
return fmt.Errorf("pagamento falhou: %w", err)
}
order.Status = domain.StatusPaid
return s.repo.Update(ctx, order)
}
func generateID() string {
return fmt.Sprintf("ORD-%d", time.Now().UnixNano())
}
Padrões de Comunicação
1. Síncrono: HTTP REST
Já coberto no guia de APIs REST. Para microserviços, adicione:
Go Testing: TDD e Benchmarks - Guia Completo
Aprenda testes em Go com TDD, testes unitários, mock, benchmarks e coverage. Tutorial completo com exemplos práticos para testar aplicações Go profissionalmente.
Testar código é essencial para garantir qualidade, confiança e manutenibilidade de software. Go possui uma biblioteca de testes robusta e bem integrada na linguagem. Neste guia completo, vamos explorar desde testes unitários básicos até TDD, mocks, benchmarks e testes de integração.
Por Que Testar em Go?
Go torna testes simples com:
- Package
testingembutido - sem dependências externas - Testes compilados - rápidos e eficientes
- Coverage nativo -
go test -cover - Benchmarks - testes de performance integrados
- Paralelismo -
t.Parallel()para testes concorrentes
Testes Unitários Básicos
Arquivos de teste terminam com _test.go:
Concorrência em Go: Goroutines e Channels - Guia Completo
Domine concorrência em Go. Tutorial completo sobre goroutines, channels, select, sync e padrões de concorrência. Exemplos práticos para iniciantes.
A concorrência é uma das features mais poderosas e distintivas de Go. Enquanto outras linguagens tornam a programação paralela complexa e cheia de boilerplate, Go a torna simples e elegante com goroutines e channels.
Neste quarto artigo da série “Golang para Iniciantes”, vamos desvendar os segredos da concorrência em Go. Você aprenderá a executar múltiplas tarefas simultaneamente de forma segura e eficiente.
Se você está chegando agora:
Controle de Fluxo em Go: if, switch, loops - Tutorial Completo
Aprenda controle de fluxo em Go. Tutorial completo sobre if/else, switch, for loops, range e controle de execução. Exemplos práticos para iniciantes.
Neste terceiro artigo da série “Golang para Iniciantes”, vamos explorar as estruturas de controle de fluxo em Go. Você aprenderá a tomar decisões com if/else, selecionar entre múltiplas opções com switch, e repetir operações com loops for.
Se você está chegando agora, recomendamos começar pela introdução:
📖 ← Artigo Anterior: Sintaxe Básica de Go
If e Else
Go tem uma sintaxe de condicionais limpa e direta.
If Básico
package main
import "fmt"
func main() {
idade := 18
if idade >= 18 {
fmt.Println("Maior de idade")
}
}
If-Else
package main
import "fmt"
func main() {
nota := 7.5
if nota >= 7.0 {
fmt.Println("Aprovado!")
} else if nota >= 5.0 {
fmt.Println("Recuperação")
} else {
fmt.Println("Reprovado")
}
}
If com Inicialização
Uma característica única de Go: você pode inicializar variáveis no if:
Criando sua Primeira API REST em Go: Tutorial Completo Passo a Passo
Construa uma API REST completa em Go. Tutorial prático com HTTP handlers, JSON, routing, middleware e banco de dados. Do zero à produção.
Neste quinto e último artigo da série “Golang para Iniciantes”, vamos aplicar tudo o que aprendemos para construir uma API REST completa do zero. Você criará um servidor web funcional com rotas, handlers, JSON, persistência em memória e testes.
Se você está chegando agora, recomendamos revisar os artigos anteriores:
📖 ← Série Completa: Golang para Iniciantes
O Que Vamos Construir
Vamos criar uma API de Gerenciamento de Tarefas (Todo API) com as seguintes funcionalidades:
Golang para Iniciantes: Primeiros Passos - Guia Completo 2026
Aprenda Go do zero. Tutorial completo de instalação, configuração do ambiente e primeiro programa. Guia passo a passo para iniciantes em Golang.
Go (ou Golang) é uma das linguagens de programação mais populares para desenvolvimento backend, microserviços e ferramentas de infraestrutura. Criada pelo Google em 2009, ela combina simplicidade, performance e produtividade de uma forma única.
Se você está começando sua jornada em Go, este é o lugar certo. Neste primeiro artigo da série “Golang para Iniciantes”, vamos cobrir tudo o que você precisa para dar os primeiros passos: desde a instalação até escrever seu primeiro programa funcional.
Sintaxe Básica de Go: Variáveis, Tipos e Funções - Tutorial Completo
Aprenda a sintaxe de Go na prática: variáveis com :=, 15 tipos de dados, funções com múltiplos retornos, structs e métodos. Exemplos que você pode copiar e rodar.
Neste segundo artigo da série “Golang para Iniciantes”, vamos mergulhar nos fundamentos da linguagem. Você aprenderá tudo sobre variáveis, tipos de dados, funções, structs e métodos — os blocos de construção essenciais de qualquer programa Go.
Se você ainda não leu o primeiro artigo, recomendamos começar por lá:
📖 ← Artigo Anterior: Golang para Iniciantes: Primeiros Passos
Variáveis em Go
Go oferece várias formas de declarar variáveis, desde a forma explícita até a inferência de tipos.