Entendendo CQRS e Event Sourcing.

Davi Orlandi - Jul 15 - - Dev Community

Introdução

Na arquitetura de software moderna, garantir escalabilidade, manutenibilidade e desempenho é crucial. Dois padrões que ajudam a alcançar esses objetivos são CQRS (Command Query Responsibility Segregation) e Event Sourcing. Este artigo explora esses conceitos, seus benefícios e como podem ser implementados.

O que é CQRS?

CQRS significa Segregação de Responsabilidade de Comando e Consulta. É um padrão que separa operações de leitura e escrita para um banco de dados. Tradicionalmente, operações CRUD (Criar, Ler, Atualizar, Deletar) são realizadas em um único modelo, o que pode levar à complexidade e ineficiência, especialmente em sistemas de grande escala.

Benefícios do CQRS

  1. Escalabilidade: Ao separar leituras e escritas, cada uma pode ser escalada independentemente.
  2. Desempenho: Os modelos de leitura podem ser otimizados para consultas, e os modelos de escrita podem ser otimizados para atualizações.
  3. Modelos Simplificados: A separação de preocupações torna a base de código mais limpa e fácil de manter.

Implementando CQRS

Em um sistema CQRS, comandos (escritas) e consultas (leituras) são manipulados por diferentes modelos:

  • Modelo de Comando: Manipula operações que mudam o estado (ex.: criar, atualizar, deletar).
  • Modelo de Consulta: Manipula operações somente de leitura (ex.: buscar dados).

Aqui está um exemplo básico usando TypeScript:

// Modelo de Comando
class CreateUserCommand {
    constructor(public name: string, public email: string) {}
}

class UserCommandHandler {
    handle(command: CreateUserCommand): void {
        // Lógica para manipular a criação do usuário
        // ex.: salvar no banco de dados
    }
}

// Modelo de Consulta
class UserQuery {
    constructor(public id: string) {}
}

class UserQueryHandler {
    handle(query: UserQuery): User | null {
        // Lógica para buscar dados do usuário
        // ex.: recuperar do banco de dados
        return { id: query.id, name: "John Doe", email: "john.doe@example.com" };
    }
}
Enter fullscreen mode Exit fullscreen mode

O que é Event Sourcing?

Event Sourcing é um padrão onde mudanças de estado são capturadas como uma sequência de eventos. Em vez de armazenar o estado atual, o sistema armazena uma série de eventos que descrevem transições de estado.

Benefícios do Event Sourcing

  1. Auditabilidade: Cada mudança é registrada, fornecendo um histórico completo.
  2. Reprodução de Eventos: O estado do sistema pode ser reconstruído reproduzindo eventos.
  3. Desacoplamento: Eventos podem ser usados para integrar com outros sistemas de forma assíncrona.

Implementando Event Sourcing

Em um sistema baseado em eventos, mudanças de estado são representadas como eventos:

// Evento
class UserCreatedEvent {
    constructor(public id: string, public name: string, public email: string) {}
}

// Armazenamento de Eventos
class EventStore {
    private events: Array<any> = [];

    save(event: any): void {
        this.events.push(event);
    }

    getEvents(): Array<any> {
        return this.events;
    }
}

// Agregado
class User {
    private id: string;
    private name: string;
    private email: string;

    constructor(private eventStore: EventStore) {}

    create(id: string, name: string, email: string): void {
        const event = new UserCreatedEvent(id, name, email);
        this.apply(event);
        this.eventStore.save(event);
    }

    private apply(event: UserCreatedEvent): void {
        this.id = event.id;
        this.name = event.name;
        this.email = event.email;
    }
}

// Uso
const eventStore = new EventStore();
const user = new User(eventStore);
user.create("1", "John Doe", "john.doe@example.com");

console.log(eventStore.getEvents());
Enter fullscreen mode Exit fullscreen mode

Combinando CQRS e Event Sourcing

CQRS e Event Sourcing se complementam bem. Event Sourcing se encaixa naturalmente no lado de escrita do CQRS, onde comandos resultam em eventos que são armazenados e processados. O lado de leitura pode usar esses eventos para construir modelos de consulta.

Exemplo

// Modelo de Comando (Baseado em Eventos)
class UserCommandHandler {
    constructor(private eventStore: EventStore) {}

    handle(command: CreateUserCommand): void {
        const event = new UserCreatedEvent(command.name, command.email);
        this.eventStore.save(event);
    }
}

// Modelo de Consulta
class UserReadModel {
    private users: { [key: string]: User } = {};

    handleEvent(event: UserCreatedEvent): void {
        this.users[event.id] = new User(event.id, event.name, event.email);
    }

    getUser(id: string): User | null {
        return this.users[id] || null;
    }
}

// Uso
const eventStore = new EventStore();
const userCommandHandler = new UserCommandHandler(eventStore);
const userReadModel = new UserReadModel();

userCommandHandler.handle(new CreateUserCommand("John Doe", "john.doe@example.com"));
eventStore.getEvents().forEach(event => userReadModel.handleEvent(event));

console.log(userReadModel.getUser("1"));
Enter fullscreen mode Exit fullscreen mode
. . . .
Terabox Video Player