![]() |
Hoje estou transcrevendo um artigo que analisa os erros comuns de implementação no padrão CQRS. (O artigo original pode ser lido neste link.) |
Fiz a transcrissão do artigo com alguns ajustes, e, onde as afirmações não me pareceram ser claras o suficiente coloquei interrogações para indicar que isso precisa ser melhor averiguado.
A maioria dos artigos sobre o padrão CQRS expõe boas teorias e implementações incorretas. Aqui, quero me concentrar nos erros comuns ao implementar esse padrão com ASP.NET Core, Entity Framework (EF) e princípios do DDD
Antes de começar, quero destacar o espírito deste padrão:
"Use um
modelo diferente para atualizar e ler informações."
Existem dois fatos importantes relacionados a esse
espírito:
1. Utilizar modelos diferentes é vantajoso apenas em sistemas
com necessidades de leitura sofisticadas.
2. A utilização de modelos
diferentes acrescenta uma complexidade arriscada, pelo que as vantagens devem
justificar essa decisão.
Para obter uma descrição completa desse padrão,
consulte este
artigo.
A maioria dos artigos sobre CQRS sugere uma solução baseada
no padrão Mediator usando a biblioteca MediatR. Alguns programadores elevam esta
abordagem comum à categoria de melhores práticas.
Vamos analisar seus problemas.
Erro 1 : Esqueçer a separação do modelo de persistência
A maioria dos artigos na web orienta você em todas as etapas necessárias para criar uma solução capaz de gerenciar fluxos de leitura e atualização separados, apenas para acabar usando o mesmo modelo de persistência. Essas soluções agregam toda a complexidade sem as vantagens que a justificam.
Mas, o que significa ter modelos separados ?
Ao usar o EF Core, a abordagem comum é implementar a
infraestrutura de persistência em um projeto separado. Este projeto implementa
um conjunto de interfaces de repositório declaradas na camada de domínio. Então
você acaba tendo algo como no exemplo exibido na
figura a seguir:

Exemplo de projeto web estruturado com conceitos de DDD.
Na figura anterior, o projeto
Ordering.Infraestructure
representa o modelo único de leitura e atualização. Se você deseja separar o
modelo de leitura e atualização, você precisa adicionar um novo projeto de
infraestrutura para o modelo de leitura.
No exemplo, Ordering.Infraestructure é definido para um projeto estruturado em DDD, portanto corresponde ao modelo de atualização. O novo modelo de leitura não precisa seguir o DDD nem usar EF, e implementa serviços de consulta dedicados que retornam Views Model, em contraste com repositórios e entidades.
Você poderia pensar que colocar repositórios e serviços de consulta no mesmo projeto é uma solução mais simples, então, por favor, leia novamente o primeiro fato sobre o espírito do CQRS.
Se você realmente tem necessidades especiais de leitura, considere alternativas melhores que usar o EF Core.
Assim, ter mais de uma biblioteca ORM ou Micro-ORM
como dependências do mesmo projeto:
• Aumenta o acoplamento
desnecessário. Se você tiver dois conjuntos distintos de classes, com
responsabilidades distintas e dependências distintas, use projetos distintos.
• Aumenta os problemas de colisão de nomes porque o ORM implementa mais ou
menos o mesmo conjunto de conceitos.
• Vai contra o S
dos princípios SOLID.
Erro 2 : Usar o CQRS por padrão
Por favor, leia novamente o primeiro fato sobre o espírito do padrão CQRS: Somente em sistemas com necessidades de leitura sofisticadas.
Poucos sistemas atendem a esse critério. Este aviso é tão importante que é
destacado em descrições com profundidade mínima: Cuidado, pois para a maioria
dos sistemas o CQRS adiciona complexidade arriscada.
Mesmo com todos os
avisos, os programadores aplicaram o CQRS porque consideraram uma boa prática.
Isso se enquadra na categoria de YAGNI (You Aren't Gonna Need It" - É
um princípio importante de desenvolvimento de software que incentiva a adição de
funcionalidades somente quando realmente necessárias.)
Erro 3 : Usar uma abordagem parcial do CQRS
Alguns programadores percebem que seus sistemas não precisam totalmente do CQRS, mas gostam do conceito de separação de comandos e consultas porque parece promissor. Portanto, eles apenas adicionam uma camada de abstração adicional usando uma implementação personalizada do Mediator ou uma biblioteca externa. Esta é uma abordagem CQRS parcial e deve ser tomada com cautela.
Mas vamos supor que seu projeto possa obter vantagens significativas introduzindo a separação de comandos e consultas. Se o sistema for construído sobre a ASP.NET Core, há um fator que geralmente passa despercebido: a ASP.NET Core já implementa o Mediator(???) e, na maioria dos casos, melhor do que qualquer implementação personalizada adicional ou biblioteca externa.
Mapear solicitações e respostas do ASP.NET Core para comandos e consultas é simples, sem a necessidade de adicionar nenhuma camada de abstração adicional.
No livro Design Patterns: Elements of Reusable Object-Oriented Software, a equipe Gang Of Four (GOF) usa este diagrama para descrever o caso de uso do padrão Mediador:

O GOF descreve o objeto Mediador nestes termos: Ele permite que outros objetos do aplicativo interajam sem conhecer as identidades uns dos outros. O mediador também encapsula um protocolo que os objetos podem seguir. Em uma aplicação web, outros objetos são clientes web e servidores web, e o protocolo é HTTP.
Na ASP.NET Core, o padrão Mediator é implementado por um Middleware (???).
Vamos compará-lo com os recursos da popular biblioteca MediatR:
• No
MediatR, o programador constrói manualmente comandos e queroes e usa um
manipulador-localizador (IMediator)
para processá-los. O programador deve registrar manualmente os manipuladores
(com comandos, consultas e possíveis respostas) no contêiner DI. O programador
pode registrar manipuladores adicionais para construir pipelines para problemas
transversais.
• Na ASP.NET Core, as solicitações são construídas,
validadas e passadas para controladores (ou endpoints individuais quando a
abordagem de APIs Minimal é usada) seguindo mecanismos de roteamento. O
programador não precisa configurar manualmente cada controlador no contêiner DI.
O programador pode registrar delegados de solicitação adicionais para configurar
o pipeline do ASP.NET Core para problemas transversais.
Portanto, projetos que podem obter vantagens significativas introduzindo a separação de comandos e consultas já podem obtê-las no ASP.NET Core.(???)
Erro 4: usar o MediatR por padrão
Vamos supor que você possa justificar o CQRS em seu projeto. Existem dois elementos a serem avaliados antes de realizar o acoplamento com a biblioteca MediatR:
• Ela não implementa o padrão Mediator(???).
Faz algo diferente. É um despachante de solicitação com esteróides. Não existe
uma dissociação real porque não existe um protocolo real. Todas as partes
comunicadas são sempre indiretamente, mas fortemente acopladas por meio de tipos
de solicitação e resposta.
• Na maioria das vezes, é mais barato implementar o Mediator você mesmo (???)
do que usar uma biblioteca pesada e completa como o MediatR. Deixe-me sugerir
este artigo.
Comentários finais
Padrões de design de nicho como CQRS foram identificados, estudados e
cuidadosamente descritos como
soluções para problemas muito específicos,
portanto, aplicá-los no problema errado ou parcialmente deve ser visto com
ceticismo.
Aplicar parcialmente o CQRS significa essencialmente abandonar
o padrão e permanecer com o princípio, CQS. Pode haver bons motivos para
trabalhar com CQS, por exemplo, adotar o
Fatiamento Vertical em nosso design. ("Vertical
Slice", é uma técnica de desenvolvimento de software que divide um projeto em
fatias verticais, cada uma contendo todas as camadas e funcionalidades
necessárias para entregar um valor completo ao usuário final.)
E estamos conversados...![]()
"Falou-lhes, pois, Jesus outra vez, dizendo: Eu sou a luz do mundo; quem me
segue não andará em trevas, mas terá a luz da vida."
João 8:12
Referências: