DEV Community

Gabriela Mendes
Gabriela Mendes

Posted on

e o tal do Microserviço?

A ideia aqui é partilhar meus estudos com vocês. Hoje começaremos por um tema básico, que todo dev deve ter na ponta da língua. O que que é esse tal de microserviço?

O que é um Microsserviço?

Microserviços (ou Microservices Architecture) é um estilo arquitetural no qual uma aplicação é estruturada como uma coleção de serviços pequenos, independentes e fracamente acoplados. Isto é, cada microserviço tem baixa dependência em relação ao outro e alta autonomia. Pensando nisso, cada serviço deve focar em uma única responsabilidade, executar seu próprio processo separadamente, comunicar-se através de APIs (com conexões HTTP/REST ou serviços de mensageria, por exemplo), ser independência para ser deployado, ter seu próprio banco de dado e ser desenvolvido por times autonômos.

O dilema entre Monolito e Microserviço

Vamos desmistificar?

Enquanto o monolito é uma aplicação única, na qual os processos de deploy e de escabalidade funcionam "numa tacada só" (tudo "deploya" e escala junto), um microserviço é independente, assim como seu deploy e escala serviços específicos. Sobre complexidade, um ponto interessante é que o monolito tende a ser inicialmente mais simples, enquanto o microserviço já é inicializado com uma complexidade maior, devido a sua natureza infraestrutural e de comunicação.
Isso vai de encontro com o que tanto ouvimos dos desenvolvedores, sobre qual é a melhor escolha: depende. Se o seu projeto não for escalar, for algo pontual, às vezes, um monolito, por sua simplicidade já basta. Agora, para muitos casos, o investimento inicial compensa.

Característica Monolito Microserviços
Estrutura Aplicação única Múltiplos serviços independentes
Deploy Tudo junto Cada serviço tem sua vez ao sol
Escalabilidade Aplicação inteira Serviços específicos
Tecnologia Limitação de stack Flexibilidade pra várias tecnologias
Complexidade Menor inicialmente Maior inicialmente (infra, comunicação...)
Times (tende a...) Time grande e centralizado Times pequenos e autônomos

Beleza, entendi. Temos mais benefícios?

Benefícios do Microserviço:

1. Escalabilidade Independente

  • Escale apenas os serviços que precisam
  • Otimize recursos e custos

2. Resiliência e Isolamento de Falhas

  • Se um serviço cai, os outros continuam funcionando
  • Fault isolation: problemas ficam contidos

3. Flexibilidade Tecnológica

  • Cada serviço pode usar a tecnologia mais adequada
  • Facilita adoção de novas tecnologias gradualmente

4. Deploy Independente

  • Atualize um serviço sem afetar os outros
  • Faça releases mais rápidos e frequentes
  • Menor risco em cada deploy

5. Organização de Times

  • Times menores e focados em domínios específicos
  • Maior autonomia e ownership
  • Desenvolvimento paralelo mais eficiente

6. Manutenibilidade

  • Código menor e mais focado
  • Mais fácil de entender e modificar
  • Tende a reduzir débito técnico

Então vou usar sempre! Tem alguma desvantagem?

Quando NÃO usar Microserviços:

Aplicações pequenas e simples
Sua configuração inicial tende a ser mais complexa e, pra aplicações pequenininhas, pode ser super desnecessário seu uso.

Falta de expertise em DevOps/infraestrutura
Essa não é EXATAMENTE uma destanvagem, mas, antes de construir seu microserviço, é essencial que sua infra seja bem pensada (e, para isso, precisamos de conhecimento sólido).

Startup em fase inicial
Essa aqui foi uma sugestão que encontrei na internet. E a justificativa é, no mínimo, interessante. Antes de escalar para microserviços, valide o produto. Valide, valide, valide! E, depois, se aventure.

Show de bola! Como posso construir um Microsserviço?

Lance mão dos princípios de Design

Domain-Driven Design (DDD)

  • Identifique os Bounded Contexts (contextos delimitados)
  • Cada microserviço representa um domínio de negócio
  • Exemplo: UserService, PaymentService, NotificationService

Single Responsibility

  • Cada serviço tem apenas uma função
  • Se precisa do "E" para descrever, provavelmente já foi além

Design pensado para Falhas

  • Assuma que tudo pode falhar (e é quase certo que algo irá!)
  • Implemente Circuit Breakers, Retries, Timeouts...

Tem mais!

Conheça um pouco sobre componentes essenciais

API Gateway

Cliente → API Gateway → [Microserviço A]
                     → [Microserviço B]
                     → [Microserviço C]
Enter fullscreen mode Exit fullscreen mode
  • Ponto de entrada único
  • Roteamento de requisições
  • Autenticação/Autorização centralizada
  • Rate limiting

2. Service Discovery

  • Serviços se registram automaticamente
  • Localização dinâmica de serviços

3. Load Balancer

  • Distribui requisições entre instâncias
  • Health checks

4. Message Broker (opcional)

  • Comunicação assíncrona
  • Ferramentas: RabbitMQ, Kafka, NATS

5. Banco de Dados por Serviço

  • Cada microserviço tem seu próprio DB
  • Database por Service Pattern

Vamos ao passo a passo?

Planejamento e Design

1. Identifique os domínios de negócio
2. Defina as responsabilidades de cada serviço
3. Desenhe as APIs e contratos
4. Defina a estratégia de comunicação (síncrona/assíncrona)
5. Planeje a estratégia de dados
Enter fullscreen mode Exit fullscreen mode

Configuração da Infraestrutura

1. Container runtime (Docker)
2. Orquestração (Kubernetes, Docker Swarm)
3. Service mesh (Istio, Linkerd) - opcional
4. Observabilidade (logs, métricas, tracing)
5. CI/CD pipeline
Enter fullscreen mode Exit fullscreen mode

Desenvolvimento do Serviço

exemplo de estrutura

microservice/
├── cmd/
   └── api/
       └── main.go          // Entry point
├── internal/
   ├── handler/             // HTTP handlers
   ├── service/             // Business logic
   ├── repository/          // Data access
   └── model/               // Domain models
├── pkg/
   └── middleware/          // Middlewares reutilizáveis
├── config/
   └── config.go            // Configs
├── Dockerfile
├── docker-compose.yml
└── go.mod
Enter fullscreen mode Exit fullscreen mode

main.go

package main

import (
    "log"
    "net/http"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()

    // Health check
    r.HandleFunc("/health", healthHandler).Methods("GET")

    // Business endpoints
    r.HandleFunc("/api/v1/users", getUsersHandler).Methods("GET")
    r.HandleFunc("/api/v1/users/{id}", getUserHandler).Methods("GET")

    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", r))
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("OK"))
}
Enter fullscreen mode Exit fullscreen mode

Alguns dos padrões essenciais:

Circuit Breaker:

import "github.com/sony/gobreaker"

var cb *gobreaker.CircuitBreaker

func init() {
    cb = gobreaker.NewCircuitBreaker(gobreaker.Settings{
        Name:        "external-service",
        MaxRequests: 3,
        Interval:    time.Minute,
        Timeout:     time.Second * 10,
    })
}

func callExternalService() error {
    _, err := cb.Execute(func() (interface{}, error) {
        return http.Get("http://external-service/api")
    })
    return err
}
Enter fullscreen mode Exit fullscreen mode

Health Check:

type HealthResponse struct {
    Status   string            `json:"status"`
    Services map[string]string `json:"services"`
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    health := HealthResponse{
        Status: "UP",
        Services: map[string]string{
            "database": checkDatabase(),
            "cache":    checkCache(),
        },
    }
    json.NewEncoder(w).Encode(health)
}
Enter fullscreen mode Exit fullscreen mode

Observabilidade

Logging estruturado:

import "go.uber.org/zap"

logger, _ := zap.NewProduction()
defer logger.Sync()

logger.Info("user created",
    zap.String("user_id", userID),
    zap.String("email", email),
)
Enter fullscreen mode Exit fullscreen mode

Métricas com Prometheus:

import "github.com/prometheus/client_golang/prometheus"

var httpRequestsTotal = prometheus.NewCounterVec(
    prometheus.CounterOpts{
        Name: "http_requests_total",
        Help: "Total number of HTTP requests",
    },
    []string{"method", "endpoint", "status"},
)

func init() {
    prometheus.MustRegister(httpRequestsTotal)
}
Enter fullscreen mode Exit fullscreen mode

Tracing distribuído:

import "go.opentelemetry.io/otel"

// Adiciona tracing aos handlers
tracer := otel.Tracer("my-service")
ctx, span := tracer.Start(r.Context(), "getUserHandler")
defer span.End()
Enter fullscreen mode Exit fullscreen mode

Containerização

Exemplo de Dockerfile:

# Multi-stage build
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main cmd/api/main.go

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]
Enter fullscreen mode Exit fullscreen mode

docker-compose.yml:

version: '3.8'

services:
  api:
    build: .
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/mydb
    depends_on:
      - db

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=mydb
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
Enter fullscreen mode Exit fullscreen mode

Segurança

Autenticação e Autorização

  • JWT tokens
  • OAuth 2.0 / OpenID Connect
  • API Keys

Comunicação Segura

  • TLS/HTTPS
  • mTLS (mutual TLS) entre serviços

Secrets Management

  • Não hardcode secrets!
  • Use: HashiCorp Vault, AWS Secrets Manager, Kubernetes Secrets

Algumas Aplicações Práticas (hipotéticas)

Cenário: E-commerce

Problema: Monolito com 500k usuários, Black Friday derruba o site.

Solução com Microserviços:

┌─────────────────┐
│   API Gateway   │
└────────┬────────┘
         │
    ┌────┴────┬────────┬──────────┬─────────┐
    │         │        │          │         │
┌───▼───┐ ┌──▼──┐ ┌───▼────┐ ┌───▼───┐ ┌──▼────┐
│ Users │ │Catalog│ │Payment│ │Orders│ │Shipping│
└───────┘ └─────┘ └────────┘ └───────┘ └───────┘
Enter fullscreen mode Exit fullscreen mode

Benefícios:

  • Escale apenas Payment e Orders durante picos
  • Se Shipping cai, usuários ainda podem comprar
  • Deploy de promoções sem afetar pagamento

Cenário: Plataforma de Streaming

Problema: Diferentes funcionalidades com cargas muito diferentes.

Microserviços:

  • Video Transcoding Service - Processa uploads (CPU intensivo)
  • Recommendation Service - ML/AI para recomendações
  • User Service - Gerencia contas e preferências
  • Streaming Service - Entrega de conteúdo (alto throughput)
  • Billing Service - Cobranças e assinaturas
  • Analytics Service - Coleta dados de visualização

Benefícios:

  • Transcoding usa GPU, Streaming usa CDN
  • Recomendações podem usar Python/TensorFlow, por exemplo
  • Streaming escala horizontalmente em eventos

Cenário: Banking/Fintech

Problema: Alta regulação, necessidade de auditoria, segurança crítica.

Microserviços:

Authentication Service (SSO)
    ↓
Account Service → Transaction Service → Notification Service
    ↓                    ↓
Audit Service ← Fraud Detection Service
Enter fullscreen mode Exit fullscreen mode

Benefícios:

  • Audit Service registra TUDO independentemente
  • Fraud Detection pode usar modelos ML complexos
  • Isolamento de falhas crítico (fraude não derruba transações)
  • Compliance mais fácil (isole serviços regulados)

Cenário: App de Delivery

Problema: Operações em tempo real, geo-localização, múltiplos atores.

Microserviços:

  • Customer Service - Pedidos dos clientes
  • Restaurant Service - Gestão de restaurantes/cardápios
  • Driver Service - Entregadores disponíveis
  • Location Service - Tracking em tempo real
  • Matching Service - Match pedido → entregador
  • Payment Service - Processamento de pagamentos
  • Notification Service - Push notifications

Comunicação:

  • REST para operações CRUD
  • WebSockets para tracking em tempo real
  • Kafka para eventos (pedido criado, entregador atribuído)

Nem tudo são flores, né?

Problemas Comuns de Microserviços em Go

Gerenciamento de Erros Distribuídos

Problema:

// Erro sem contexto propagado
func GetUser(id string) (*User, error) {
    user, err := repo.FindByID(id)
    if err != nil {
        return nil, err  // Perdeu o contexto!
    }
    return user, nil
}
Enter fullscreen mode Exit fullscreen mode

Solução:

//Com contexto e wrapping
import "fmt"

func GetUser(id string) (*User, error) {
    user, err := repo.FindByID(id)
    if err != nil {
        return nil, fmt.Errorf("failed to get user %s: %w", id, err)
    }
    return user, nil
}

// Plus: com pkg/errors
import "github.com/pkg/errors"

func GetUser(id string) (*User, error) {
    user, err := repo.FindByID(id)
    if err != nil {
        return nil, errors.Wrap(err, "repository.FindByID failed")
    }
    return user, nil
}
Enter fullscreen mode Exit fullscreen mode

Context Propagation (Tracing)

Problema:

// Sem propagação de contexto
func ProcessOrder(orderID string) error {
    user := getUserFromDB(orderID)      // Perdeu trace
    payment := processPayment(user.ID)   // Perdeu trace
    return nil
}
Enter fullscreen mode Exit fullscreen mode

Solução:

func ProcessOrder(ctx context.Context, orderID string) error {
    user, err := getUserFromDB(ctx, orderID)
    if err != nil {
        return err
    }

    payment, err := processPayment(ctx, user.ID)
    if err != nil {
        return err
    }

    return nil
}
Enter fullscreen mode Exit fullscreen mode

Timeout e Cancelamento

Problema:

// Chamada HTTP sem timeout
resp, err := http.Get("http://other-service/api")
Enter fullscreen mode Exit fullscreen mode

Solução:

client := &http.Client{
    Timeout: 5 * time.Second,
}

ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

req, _ := http.NewRequestWithContext(ctx, "GET", "http://other-service/api", nil)
resp, err := client.Do(req)
Enter fullscreen mode Exit fullscreen mode

Goroutine Leaks em Services

Problema:

// Goroutine sem controle
func HandleRequest(w http.ResponseWriter, r *http.Request) {
    go func() {
        // Se a requisição cancelar, isso continua rodando!
        result := doHeavyWork()
        // Pode causar leak
    }()
}
Enter fullscreen mode Exit fullscreen mode

Solução:

// Com context e, também, controle
func HandleRequest(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()

    done := make(chan Result)
    go func() {
        result := doHeavyWork()
        select {
        case done <- result:
        case <-ctx.Done():
            return  // Cancela se request cancelar
        }
    }()

    select {
    case result := <-done:
        json.NewEncoder(w).Encode(result)
    case <-ctx.Done():
        http.Error(w, "request cancelled", http.StatusRequestTimeout)
    }
}
Enter fullscreen mode Exit fullscreen mode

Database Connection Pooling

Problema:

// Abrir conexão a cada request
func GetUser(id string) (*User, error) {
    db, _ := sql.Open("postgres", dsn)  // Abre nova conexão!
    defer db.Close()
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Solução:

// Connection pool global
var db *sql.DB

func init() {
    var err error
    db, err = sql.Open("postgres", dsn)
    if err != nil {
        log.Fatal(err)
    }

    // Configurar pool
    db.SetMaxOpenConns(25)
    db.SetMaxIdleConns(5)
    db.SetConnMaxLifetime(5 * time.Minute)
}

func GetUser(ctx context.Context, id string) (*User, error) {
    var user User
    err := db.QueryRowContext(ctx, "SELECT * FROM users WHERE id = $1", id).Scan(&user)
    return &user, err
}
Enter fullscreen mode Exit fullscreen mode

Graceful Shutdown

Problema:

// Server sem graceful shutdown
func main() {
    http.ListenAndServe(":8080", handler)
}
// Se matar o processo, conexões serão perdidas
Enter fullscreen mode Exit fullscreen mode

Solução:

// Graceful shutdown completo
func main() {
    server := &http.Server{
        Addr:    ":8080",
        Handler: handler,
    }

    // Inicia server em goroutine
    go func() {
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("Server error: %v", err)
        }
    }()

    // Espera sinal de shutdown
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    log.Println("Shutting down server...")

    // Timeout para shutdown
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    if err := server.Shutdown(ctx); err != nil {
        log.Fatal("Server forced to shutdown:", err)
    }

    log.Println("Server exited")
}
Enter fullscreen mode Exit fullscreen mode

Circuit Breaker não implementado

Problema:

  • Serviço A chama Serviço B
  • Serviço B está lento
  • Serviço A entra em looping de retry
  • Cascading failure!

Solução:

import (
    "github.com/sony/gobreaker"
    "time"
)

var cb *gobreaker.CircuitBreaker

func init() {
    var st gobreaker.Settings
    st.Name = "HTTP Request"
    st.MaxRequests = 3
    st.Interval = time.Minute
    st.Timeout = 10 * time.Second
    st.ReadyToTrip = func(counts gobreaker.Counts) bool {
        failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
        return counts.Requests >= 3 && failureRatio >= 0.6
    }

    cb = gobreaker.NewCircuitBreaker(st)
}

func CallExternalService() (interface{}, error) {
    return cb.Execute(func() (interface{}, error) {
        resp, err := http.Get("http://external-service/api")
        if err != nil {
            return nil, err
        }
        defer resp.Body.Close()

        if resp.StatusCode != 200 {
            return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
        }

        return resp, nil
    })
}
Enter fullscreen mode Exit fullscreen mode

Falta de Structured Logging

Problema:

// Logs não estruturados
log.Println("User created: " + userID)
log.Println("Error: " + err.Error())
Enter fullscreen mode Exit fullscreen mode

Solução:

// Structured logging com zap
import "go.uber.org/zap"

logger, _ := zap.NewProduction()
defer logger.Sync()

logger.Info("user created",
    zap.String("user_id", userID),
    zap.String("email", email),
    zap.Time("created_at", time.Now()),
)

logger.Error("failed to process payment",
    zap.Error(err),
    zap.String("payment_id", paymentID),
    zap.String("user_id", userID),
)
Enter fullscreen mode Exit fullscreen mode

Configuração Hardcoded

Problema:

// Configuração hardcoded
const DatabaseURL = "postgres://localhost:5432/mydb"
const RedisURL = "localhost:6379"
Enter fullscreen mode Exit fullscreen mode

Solução:

// Configuração via environment
import "github.com/kelseyhightower/envconfig"

type Config struct {
    DatabaseURL string `envconfig:"DATABASE_URL" required:"true"`
    RedisURL    string `envconfig:"REDIS_URL" default:"localhost:6379"`
    Port        int    `envconfig:"PORT" default:"8080"`
    LogLevel    string `envconfig:"LOG_LEVEL" default:"info"`
}

func LoadConfig() (*Config, error) {
    var cfg Config
    err := envconfig.Process("", &cfg)
    if err != nil {
        return nil, err
    }
    return &cfg, nil
}
Enter fullscreen mode Exit fullscreen mode

JSON Marshal/Unmarshal em Hot Path

Problema:

// Parsing JSON repetidas vezes
func HandleRequest(w http.ResponseWriter, r *http.Request) {
    var req Request
    json.NewDecoder(r.Body).Decode(&req)  // Aloca memória

    json.NewEncoder(w).Encode(response)   // Aloca memória
}
Enter fullscreen mode Exit fullscreen mode

Solução:

// Buffers com sync.Pool
var bufferPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

func HandleRequest(w http.ResponseWriter, r *http.Request) {
    buf := bufferPool.Get().(*bytes.Buffer)
    defer func() {
        buf.Reset()
        bufferPool.Put(buf)
    }()

    // Ou use jsoniter para performance
    import jsoniter "github.com/json-iterator/go"
    var json = jsoniter.ConfigCompatibleWithStandardLibrary
}
Enter fullscreen mode Exit fullscreen mode

E esse foi o estudo de hoje.

Espero que tu, que chegou até aqui, tenha aprendido ou, se já sabia, relembrado sobre esse tal de microserviço, viu?

Ah, pra fechar, deixo uns dois links interessantes, que se destacaram pra mim nos estudos:

4 Microservices Examples: Amazon, Netflix, Uber, and Etsy (https://blog.dreamfactory.com/microservices-examples)

Microservices: Is It Worth the Trouble? (https://dev.to/mygames/microservices-is-it-worth-the-trouble-3a69)

Bons estudos! E bora praticar!

Top comments (0)