DEV Community

Davi Orlandi
Davi Orlandi

Posted on

2

Entendendo CQRS e Event Sourcing.

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

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay