Hoje vamos recordar os fundamentos do CQRS e do Event Sourcing. |
Com a popularização dos microsserviços, muitos projetos estão mgirando do
monolíto para os microsserviços, e com isso, vários padrões e novas
arquiteturas projetadas especificamente para microsserviços vieram à tona.
Dentre eles podemos destacar o CQRS com event sourcing.
Antes de iniciar vamos apresentar o problema que o CQRS pretende resolver.
O problema
Nas arquiteturas tradicionais, o mesmo modelo de dados é usado para consultar e atualizar um banco de dados. Isso é simples e funciona bem para operações básicas de CRUD.
Em aplicações mais complexas, no entanto, essa abordagem pode se tornar difícil de gerenciar. Por exemplo, no lado da leitura, o aplicativo pode realizar muitas consultas diferentes, retornando objetos de transferência de dados (DTOs) com formas diferentes e assim o mapeamento de objetos pode se tornar complicado.
No lado da gravação,
o modelo pode implementar validação complexa e lógica de negócios. Como
resultado, você pode acabar com um modelo excessivamente complexo que realiza
muitas tarefas.
As cargas de trabalho de leitura e gravação geralmente são assimétricas, com
requisitos de desempenho e escala muito diferentes.
A Solução
O CQRS - Command query responsibility segregation - é um tipo específico de arquitetura de aplicativo em que as operações de gravação e leitura de um aplicativo são separadas em 2 aplicativos diferentes (a parte de comando e consulta). Com cada um tendo seus próprios bancos de dados e os aplicativos sendo conectados uns aos outros usando um canal de mensagem.
O CQRS separa as leituras e gravações em diferentes modelos, usando comandos para atualizar dados e consultas para ler dados. Assim :
Os comandos devem ser baseados em tarefas, em vez de centrados em dados;
Os comandos podem ser colocados em uma fila para processamento assíncrono, em vez de serem processados de forma síncrona;
As consultas nunca modificam o banco de dados. Uma consulta retorna um DTO que não encapsula nenhum conhecimento de domínio;
A parte de comando do aplicativo nunca retorna nenhum dado ao cliente, apenas a parte de consulta pode retornar dados aos clientes solicitantes.
Os modelos podem ser isolados, conforme mostrado no diagrama a seguir, embora isso não seja um requisito absoluto.
Ter modelos de consulta e atualização separados simplifica o design e a implementação. No entanto, uma desvantagem é que o código CQRS não pode ser gerado automaticamente a partir de um esquema de banco de dados usando mecanismos de Scaffold, como ferramentas ORM.
A característica distintiva do CQRS é que a parte de comando do aplicativo
armazena os dados de maneira otimizada para gravação e a parte de consulta
armazena os dados de maneira otimizada para leitura. Além disso, como as
operações de leitura e gravação são separadas em 2 aplicativos, elas também
podem ser dimensionadas separadamente, dependendo da carga de leitura e
gravação.
Para maior isolamento, você pode separar fisicamente os dados de leitura dos dados de gravação. Nesse caso, o banco de dados lido pode usar seu próprio esquema de dados otimizado para consultas. Por exemplo, ele pode armazenar uma view materializada dos dados, a fim de evitar junções complexas ou mapeamentos O/RM complexos. Pode até usar um tipo diferente de armazenamento de dados.
A separação dos
armazenamentos de leitura e gravação também permite que cada um seja
dimensionado adequadamente para corresponder à carga. Por exemplo, os
armazenamentos de leitura normalmente encontram uma carga muito maior do que os
armazenamentos de gravação.
O importante a entender ao usar a arquitetura CQRS é que se trata de um sistema
eventualmente consistente, ou seja: a parte de consulta levará tempo para ser
recuperada após a execução de um comando ser concluída.
Os benefícios do CQRS incluem:
Escala independente. O CQRS permite que as cargas de trabalho de leitura e gravação sejam dimensionadas de forma independente e pode resultar em menos contenções de bloqueio.
Esquemas de dados otimizados. O lado de leitura pode usar um esquema otimizado para consultas, enquanto o lado de gravação usa um esquema otimizado para atualizações.
Segurança. É mais fácil garantir que apenas as entidades de domínio corretas estejam realizando gravações nos dados.
Separação de preocupações. Segregar os lados de leitura e gravação pode resultar em modelos mais fáceis de manter e flexíveis. A maior parte da lógica de negócios complexa vai para o modelo de gravação.
O modelo de leitura pode ser relativamente simples.
Consultas mais simples. Ao armazenar uma visualização materializada no banco de dados de leitura, o aplicativo pode evitar junções complexas ao consultar.
Algumas implementações do CQRS usam Event Sourcing.
Event Sourcing
Com o padrão Event Sourcing o estado do aplicativo é armazenado como uma sequência de eventos. Cada evento representa um conjunto de alterações nos dados. O estado atual é construído pela repetição dos eventos.
Este é um tipo específico de arquitetura de
aplicação onde cada mudança feita no banco de dados também é registrada como um
evento em um armazenamento de eventos, onde é armazenado permanentemente. Nessa
arquitetura, o estado do banco de dados do aplicativo deve ser recriado
reprocessando os eventos no armazenamento de eventos em ordem.
A vantagem dessa arquitetura é que ela cria um log de eventos que pode ser usado
para determinar como o banco de dados do aplicativo atingiu o estado atual ou
para reverter o estado do banco de dados de volta a um ponto específico no
tempo, reprocessando as mensagens até esse estado específico. ponto.
Outra grande vantagem aqui é que registrar as alterações do banco de dados como
eventos nos permite reter dados que seriam perdidos de outra forma por uma
gravação ou atualização do banco de dados.
Pense em um cenário em que você está executando um aplicativo de comércio eletrônico, o usuário adiciona determinados itens ao carrinho e os remove para substituí-los por outro item semelhante. Em um sistema tradicional, as informações sobre a adição do primeiro item seriam perdidas quando o usuário o removesse do carrinho, mas em um sistema originado de eventos, a loja de eventos terá todos esses detalhes. Isso permite que o aprendizado de máquina e a IA sejam usados para processar o armazenamento de eventos e gerar informações valiosas sobre o comportamento do usuário.
CQRS com Event Sourcing
Assim, combinando as 2 arquiteturas de aplicativos, CQRS e Event sourcing, podemos construir um aplicativo com os benefícios de ambos.
Nesta arquitetura, dividimos o aplicativo
em 2 partes, a parte de comando e a parte de consulta. A parte de comando e
consulta se comunicará usando um canal de mensagens (RabbitMQ, AWS SQS, etc)
ou stream broker (Apache Kafka).
Nesta arquitetura, o executor de comandos estará gerando eventos usando um
gerador de eventos que armazenará os eventos em um armazenamento de eventos, que
geralmente é implementado usando um banco de dados SQL ou um banco de dados
NoSql, e depois envia para a parte de consulta da aplicação através do canal
de mensagens.
Na parte de consulta do aplicativo, haverá manipuladores de eventos que escutam
as mensagens postadas no canal de mensagens e atualizam o banco de dados usado
pelo manipulador de consultas para gerar respostas para solicitações de
clientes.
As vantagens desta arquitetura são :
Trilha de auditoria detalhada de como o estado atual do banco de dados foi alcançado. Isso pode ser importante para alguns aplicativos confidenciais, como softwares bancários e financeiros.
O banco de dados lido pode ser recriado reproduzindo os eventos do armazenamento de eventos em ordem;
Dimensionamento independente da parte de leitura e gravação do aplicativo, pois são divididos em 2 microsserviços. Isso é benéfico porque na maioria das aplicações um deles domina o outro.
As desvantagens desta arquitetura são :
Aplicativos mais complexos e difíceis de manter
- Essa arquitetura trará muita sobrecarga de desenvolvimento para o
aplicativo;
Consistência eventual - Se você separar os bancos de dados de leitura e gravação, os dados de leitura poderão ficar obsoletos. O armazenamento do modelo de leitura deve ser atualizado para refletir as alterações no armazenamento do modelo de gravação e pode ser difícil detectar quando um usuário emitiu uma solicitação com base em dados de leitura obsoletos.
E estamos conversados...
"Miserável homem que eu sou! quem me livrará do corpo desta morte ?
Dou
graças a Deus por Jesus Cristo nosso Senhor. Assim que eu mesmo com o
entendimento sirvo à lei de Deus, mas com a carne à lei do pecado. "
Romanos 7:24,25
Referências: