Pular para conteúdo

Aula 17 - Padrões Arquiteturais Avançados (CQRS e Event Sourcing) 🏛️

Objetivo Pedagógico

Objetivo: Arquitetura corporativa desacoplada com Command Query Responsibility Segregation (CQRS), armazenamento imutável de eventos com Event Sourcing e consistência eventual.


📑 1. Fundamentos Teóricos & Análise Técnica

Sistemas empresariais de alta complexidade frequentemente sofrem quando um único modelo de dados relacional é forçado a atender simultaneamente às demandas de escrita transacional concorrente e às necessidades de consultas analíticas pesadas.

O padrão CQRS (Command Query Responsibility Segregation) separa radicalmente essas duas responsabilidades: 1. Lado de Comando (Write Side): Otimizado para consistência forte, regras de negócio ricas e validação de invariantes de domínio. 2. Lado de Consulta (Read Side): Otimizado para velocidade de leitura, utilizando projeções desnormalizadas prontas para consumo (muitas vezes em bancos de leitura rápida como Elasticsearch ou Redis).

Essa separação atinge seu potencial máximo quando combinada com Event Sourcing: Em vez de persistir apenas o "estado atual" do registro (sobrescrevendo dados com UPDATE), o sistema persiste uma sequência cronológica e imutável de Eventos de Domínio (OrderCreated, PaymentReceived, ItemShipped). O estado atual de qualquer entidade é recalculado através do replay de sua cadeia histórica de eventos (Event Stream).

📐 Arquitetura Conceitual & Diagrama de Fluxo

graph TD
    Client["Cliente"] -->|Comando: Criar Pedido| CommandHandler["Command Handler (Write Side)"]
    CommandHandler --> Aggregate["Agregado de Domínio"]
    Aggregate -->|Gera Evento| EventStore["Event Store Imutável (Append-Only)"]
    EventStore -->|Projeção Assíncrona| Projector["Event Projector"]
    Projector --> ReadDB["Banco de Leitura Desnormalizado (Read Side)"]
    Client -->|Consulta: Buscar Pedido| QueryHandler["Query Handler"]
    QueryHandler --> ReadDB
    style CommandHandler fill:#fff3e0,stroke:#e65100
    style EventStore fill:#e8f5e9,stroke:#2e7d32
    style ReadDB fill:#e1f5fe,stroke:#01579b

🔍 Pilares e Diretrizes Técnicas

Nesta unidade, aprofundamos os seguintes conceitos fundamentais: - Trilha de Auditoria Natural (Audit Trail): Como nenhum dado é deletado ou sobrescrito, o histórico completo de mutações é preservado nativamente. - Consistência Eventual (Eventual Consistency): O lado de leitura sincroniza assincronamente com o lado de escrita via mensageria. - Viagem no Tempo (Time Travel Debugging): Capacidade de reconstituir com precisão o estado exato de qualquer conta em qualquer instante do passado. - Escalabilidade Independente: Capacidade de escalar 10 instâncias de leitura para cada instância de escrita.


🛠️ 2. Implementação Prática em Arquitetura de Software e Padrões Corporativos

Abaixo está a implementação técnica de referência, estruturada com padrões de engenharia de software e foco em robustez:

// event_sourcing_account.cs (Agregado de Conta com Event Sourcing em C#)
using System;
using System.Collections.Generic;

public record AccountOpenedEvent(Guid Id, string Owner);
public record MoneyDepositedEvent(Guid Id, decimal Amount);

public class BankAccountAggregate
{
    public Guid Id { get; private set; }
    public decimal Balance { get; private set; }
    private final List<object> _uncommittedEvents = new();

    // Reconstrói o estado aplicando a cadeia de eventos históricos
    public void LoadFromHistory(IEnumerable<object> history)
    {
        foreach (var @event in history)
        {
            ApplyChange(@event, isNew: false);
        }
    }

    public void Deposit(decimal amount)
    {
        if (amount <= 0) throw new ArgumentException("Depósito deve ser positivo.");
        ApplyChange(new MoneyDepositedEvent(Id, amount), isNew: true);
    }

    private void ApplyChange(object @event, bool isNew)
    {
        // Mutação de estado baseada no tipo do evento
        switch (@event)
        {
            case AccountOpenedEvent e:
                Id = e.Id;
                Balance = 0;
                break;
            case MoneyDepositedEvent e:
                Balance += e.Amount;
                break;
        }
        if (isNew) _uncommittedEvents.Add(@event);
    }
}

💡 Análise Passo a Passo do Código

  1. Eventos Imutáveis: AccountOpenedEvent e MoneyDepositedEvent representam fatos consumados que nunca sofrem alteração.
  2. Método LoadFromHistory: Reconstitui o saldo de qualquer conta aplicando a sequência cronológica dos fatos passados.
  3. Isolamento de Estado: O agregado garante que nenhuma regra de negócio seja violada antes de emitir um novo evento.

🎯 3. Próximos Passos & Sequência Didática