CQRS e Event Sourcing em Go: arquitetura para sistemas complexos

Treinador Online de Python para Iniciantes

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

Start Course

Quando o CRUD comum já não é suficiente: conhecendo CQRS e Event Sourcing

\n

Imagine que você está construindo um sistema de reserva de ingressos. Tudo é simples enquanto há dez usuários. Mas quando se tornam mil, e depois um milhão, começam os problemas: bancos de dados são bloqueados, consultas entram em conflito e o histórico de alterações não pode ser recuperado. Situação familiar?

\n

No mundo do desenvolvimento Go, onde desempenho e concorrência são tudo, dois padrões arquiteturais ajudam a resolver esses problemas: CQRS (Command Query Responsibility Segregation) e Event Sourcing. Hoje vamos analisá-los na prática — com código, exemplos e cenários reais de uso.

\n\n

O que é CQRS em termos simples?

\n

CQRS é a separação de responsabilidades entre comandos (escrita) e consultas (leitura). No CRUD clássico, um único modelo de dados é responsável tanto pela criação quanto pela leitura. No CQRS, criamos dois modelos separados:

\n
    \n
  • Modelo de Comando — responsável por alterar o estado (criação, atualização, exclusão)
  • \n
  • Modelo de Consulta — responsável apenas pela leitura de dados
  • \n
\n

Isso permite otimizar cada lado de forma independente: a escrita pode ser estritamente validada, e a leitura pode ser desnormalizada e rápida. Em Go, isso é especialmente eficiente graças às goroutines e canais.

\n\n

Exemplo simples de CQRS em Go

\n

Vamos criar um sistema básico para gerenciamento de pedidos. Começamos com os comandos:

\n\n
type CreateOrderCommand struct {\n    OrderID   string\n    UserID    string\n    ProductID string\n    Amount    float64\n}\n\ntype CommandHandler struct {\n    repo *OrderRepository\n}\n\nfunc (h *CommandHandler) HandleCreateOrder(ctx context.Context, cmd CreateOrderCommand) error {\n    order := Order{\n        ID:        cmd.OrderID,\n        UserID:    cmd.UserID,\n        ProductID: cmd.ProductID,\n        Amount:    cmd.Amount,\n        Status:    "pending",\n        CreatedAt: time.Now(),\n    }\n    return h.repo.Save(ctx, order)\n}
\n\n

Agora criamos um modelo separado para leitura:

\n\n
type OrderQuery struct {\n    repo *ReadRepository\n}\n\ntype OrderView struct {\n    OrderID    string\n    UserName   string\n    ProductName string\n    Total      float64\n    Status     string\n}\n\nfunc (q *OrderQuery) GetOrderSummary(ctx context.Context, orderID string) (*OrderView, error) {\n    return q.repo.GetOrderView(ctx, orderID)\n}
\n\n

Observe: em OrderView já existem dados desnormalizados (nome do usuário, nome do produto), que são coletados de diferentes fontes. Isso acelera a leitura dezenas de vezes.

\n\n

Event Sourcing: registramos não o estado, mas os eventos

\n

Event Sourcing muda a abordagem de armazenamento de dados: em vez de armazenar o estado atual de um objeto, armazenamos a sequência de eventos que levaram a esse estado. Isso dá um superpoder: histórico completo de alterações e a capacidade de "rebobinar" o estado.

\n\n

Implementação de Event Sourcing em Go

\n

Começamos com os tipos básicos de eventos:

\n\n
type Event struct {\n    ID        string\n    AggregateID string\n    Type      string\n    Data      []byte\n    Timestamp time.Time\n    Version   int\n}\n\ntype EventStore interface {\n    Save(ctx context.Context, events []Event) error\n    Load(ctx context.Context, aggregateID string) ([]Event, error)\n}
\n\n

Agora implementamos o agregado — o objeto que restaura seu estado a partir dos eventos:

\n\n
type OrderAggregate struct {\n    ID     string\n    Status string\n    Items  []OrderItem\n    Version int\n}\n\nfunc (a *OrderAggregate) Apply(event Event) {\n    switch event.Type {\n    case "OrderCreated":\n        var data OrderCreatedData\n        json.Unmarshal(event.Data, &data)\n        a.ID = data.OrderID\n        a.Status = "created"\n    case "OrderConfirmed":\n        a.Status = "confirmed"\n    case "OrderShipped":\n        a.Stat                

Blogs

Book Recommendations