O que é CQRS e como implementar? – c# design-pattern

Pergunta:


CQRS (Command and Query Responsibility Segregation) é uma dessas siglas que nos deparamos e não conseguimos compreender completamente seu significado e utilização.

O que é CQRS e como implementar?

Autor da pergunta rubStackOverflow

Comunidade

CQRS

Command Query Responsibility Segregation, ou CQRS, é um padrão de arquitetura de desenvolvimento de software que resume a separar a leitura e a escrita em dois modelos: query e command, uma para leitura e outra para escrita de dados, respectivamente.

Reprodução / Mike Fowler

  • Query model: responsável pela leitura dos dados do BD e pela atualização da interface gráfica.

  • Command model: responsável pela escrita de dados no BD e pela validação dos dados. Sua interação com a interface gráfica é somente receber os dados a serem escritos.


Hoje, aplicações modernas tem conceitos novos de disponibilidade, performance, funcionalidade e usabilidade (estamos falando de aplicações de “escritório”).

Seguinte tal estrutura, esse tipo de aplicação deve estar disponível em uma arquitetura pensada para suportar milhares de usuários, como pouquíssimos usuários (e claro, ainda nos padrões da estrutura moderna de aplicações). Querendo ou não, é um conceito de ESCALABILIDADE, o seu software/serviço seguindo o seu próprio uso.

Quando falamos da escalabilidade do software em si estamos falando
sobre ter um código e uma arquitetura que é fácil de dar manutenção,
de aumentar suas funcionalidades, de várias pessoas trabalharem nele.

Eventualmente podemos usar o termo para indicar que o software pode
ser usado por uma grande quantidade de usuários (clientes). No sentido
de pessoas ou empresas
Fonte

Num exemplo simples: pensemos num saque no caixa eletrônico. Milhares de pessoas (e agora você) acessando o mesmo banco de dados, gravando e lendo dados a todo o tempo. Ignorando as filas, imagine se demorasse horas para fazer um único saque?! Um caos.

A respeito do CQRS, outra definição importante é: nada é o que parece. É, exatamente. Se você efetua uma consulta no banco de dados (query model) e por algum motivo esses dados forem alterados (command model), o que você vê não é necessariamente o dado atualizado. Trabalhar com esses dados sempre atualizados pode causar uma enorme perda de performance (lembrando, sua aplicação é moderna!).

A proposta de dividir as tarefas pode parecer um pouco mais interessante agora. Mas não, não é só isso, a segregação dos modelos diz respeito a separação física e/ou lógica dos dados.


Numa diagramação mais simples possível do CQRS, temos:

Implementação

Não existe uma única maneira de implementar o CQRS na sua aplicação,
pode ser feito de uma forma simples ou muito complexa, depende da
necessidade. Independente de como for implementado o CQRS sempre
acarreta numa complexidade extra e por isso é necessário avaliar os
cenários em que realmente são necessários trabalhar com este padrão.
Fonte

Exemplo de implementação

Daqui para baixo, um padrão CQRS simples. Já adianto, retirado daqui.

A arquitetura CQRS para um projeto ASP.NET MVC

Crie alguns projetos de biblioteca de classes, pilha de consulta e pilha de comando, e faça referência aos dois do projeto de servidor da Web principal.

A pilha de consulta

A biblioteca de classes de pilha de consulta só está preocupada com a recuperação de dados. Ela usa um modelo de dados que corresponde aos dados usados na camada de apresentação o mais próximo possível. Você raramente precisa quaisquer regras de negócios aqui, pois elas se aplicam aos comandos que alteram o estado.

O padrão de modelo de domínio popularizado pelo DDD é essencialmente uma forma de organizar a lógica do domínio. Quando você faz consultas de front-end, você está lidando apenas com parte da lógica do aplicativo e casos de uso. A lógica de negócios do termo geralmente resulta da união de lógica específica do aplicativo com a lógica invariável de domínio. Se você souber o formato de persistência e o formato de apresentação, basta mapear os dados como faria em uma consulta do bom e velho ADO.NET/SQL.

É útil se lembrar de que qualquer código que você pode chamar da camada de aplicativo representa o domínio de negócios do sistema. Portanto, é a API invariável que expressa a lógica principal do sistema. Em condições ideais, você deve garantir que nenhuma operação inconsistente e incongruente seja possível mesmo por meio da API exposta. Então para impor a natureza somente leitura da pilha de consulta, adicione uma classe wrapper em torno do objeto de contexto do Entity Framework padrão para se conectar ao banco de dados, conforme mostrado no código a seguir:

public class Database : IDisposable
{
  private readonly QueryDbContext _context = new QueryDbContext();
  public IQueryable<Customer> Customers
  {
    get { return _context.Customers; }
  }
  public void Dispose()
  {
   _context.Dispose();
  }
}

A classe Matches é implementada como uma coleção DbSet na classe base DbContext. Como tal, ela fornece acesso completo ao banco de dados subjacente e você pode usá-la para configurar consultas e atualizar operações por meio de LINQ às Entidades.

A etapa fundamental para configurar um pipeline de consulta é permitir o acesso ao banco de dados somente para consultas. Essa é a função da classe wrapper, onde Matches é exposta como um IQueryable. A camada de aplicativo usará a classe de wrapper do banco de dados para implementar consultas direcionadas em trazer os dados para a apresentação:

var model = new RegisterViewModel();
using (var db = new Database())
{
  var list = (from m in db.Customers select m).ToList();
  model.ExistingCustomers = list;
}

Agora há uma conexão direta entre a fonte de dados e a apresentação. Você está apenas lendo e formatando os dados para fins de exibição. Você espera que a autorização seja imposta na porta por meio de logons e restrições de interface do usuário. Caso contrário, você pode adicionar mais camadas ao longo do processo e habilitar a troca de dados por meio de coleções de dados IQueryable. O modelo de dados é o mesmo que o banco de dados e é de 1 para 1 com persistência. Esse modelo é às vezes chamado de árvores de expressão em camadas (LET).

Há algumas coisas que você deve observar neste momento. Primeiro, você está no pipeline de leitura em que as regras de negócios normalmente não existem. Tudo o que você pode ter aqui são filtros e regras de autorização. Essas são bem-conhecidas no nível de camada de aplicativo. Você não precisa lidar com objetos de transferência de dados ao longo do caminho. Você tem um modelo de persistência e contêineres de dados reais para o modo de exibição. No serviço de aplicativo, você acaba com o seguinte padrão:

var model = SpecificUseCaseViewModel();
model.SomeCollection = new Database()
     .SomeQueryableCollection
     .Where(m => SomeCondition1)
     .Where(m => SomeCondition2)
     .Where(m => SomeCondition3)
     .Select(m => new SpecificUseCaseDto
       {
         // Fill up
       })
     .ToList();
return model;

Todos os objetos de transferência de dados especiais no trecho de código são específicos para o caso de uso de apresentação que você está implementando. Elas são apenas o que o usuário deseja ver no modo de exibição Razor, que você está criando e as classes são inevitáveis. Além disso, você pode substituir todas as cláusulas Where com métodos de extensão IQueryable ad hoc e ativar todo o código na caixa de diálogo escrita em linguagem específica do domínio.

A segunda coisa a observar sobre a pilha de consulta é relacionada a persistência. Na forma mais simples de CQRS, as pilhas de comando e consulta compartilham o mesmo banco de dados. Essa arquitetura torna o CQRS semelhante aos sistemas CRUD clássicos. Isso facilita a adotá-lo quando as pessoas são resistentes a alteração. No entanto, você pode criar o back-end para que as pilhas de comando e consulta tenham seus próprios bancos de dados otimizados para suas finalidades específicas. Sincronizar dois bancos de dados, em seguida, torna-se a outro problema.

A pilha de comando

No CQRS, a pilha de comando diz respeito apenas a execução de tarefas que modificam o estado do aplicativo. A camada de aplicativo recebe solicitações da apresentação e organiza a execução enviando comandos no pipeline. A expressão “enviar comandos ao pipeline” é a origem de vários tipos de CQRS.

No caso mais simples, enviar um comando consiste em simplesmente invocar um script de transação. Esse dispara um fluxo de trabalho simples que realiza todas as etapas exigidas pela tarefa. Enviar um comando da camada de aplicativo pode ser tão simples quanto o código a seguir:

public void Register(RegisterInputModel input)
{
  // Push a command through the stack
  using (var db = new CommandDbContext())
  {
    var c = new Customer {
      FirstName = input.FirstName,
      LastName = input.LastName };
    db.Customers.Add(c);
    db.SaveChanges();
  }
}

Se necessário, você pode transferir o controle para a camada de domínio verdadeiro com serviços e o modelo de domínio onde você implementa a lógica de negócios completa. No entanto, usar o CQRS não vincula você necessariamente ao DDD e coisas como agregações, fábricas e objetos de valor. Você pode ter os benefícios da separação de comando/consulta sem a complexidade extra de um modelo de domínio.

Mais links sobre implementação

De acordo com o autor do padrão ele consiste apenas em separar as operações de leitura das operações de escrita.

When most people talk about CQRS they are really speaking about
applying the CQRS pattern to the object that represents the service
boundary of the application. Consider the following pseudo-code
service definition.

CustomerService

void MakeCustomerPreferred(CustomerId)

Customer GetCustomer(CustomerId)

CustomerSet GetCustomersWithName(Name)

CustomerSet GetPreferredCustomers()

void ChangeCustomerLocale(CustomerId, NewLocale)

void CreateCustomer(Customer)

void EditCustomerDetails(CustomerDetails)

Applying CQRS on this would result in two services

CustomerWriteService

void MakeCustomerPreferred(CustomerId)

void ChangeCustomerLocale(CustomerId, NewLocale)

void CreateCustomer(Customer)

void EditCustomerDetails(CustomerDetails)

CustomerReadService

Customer GetCustomer(CustomerId)

CustomerSet GetCustomersWithName(Name)

CustomerSet GetPreferredCustomers()

That is it. That is the entirety of the CQRS pattern. There is nothing
more to it than that…

Tradução

Quando a maior parte das pessoas falam sobre CQRS elas realmente estão
falando em como aplicar o padrão CQRS a um objeto que representa a
camada de serviço duma aplicação. Considere a seguinte definição de
serviço.

Aplicar o padrão CQRS resultaria em dois serviços

….

É só isto mesmo. O padrao CQRS é sómente isso. Não há mais nada nele para
além disso…

Fonte – O próprio criador do padrão escreveu este texto.

Como a questão contem a tag C#, o material mais oficial que encontrei é:

A princípio CQRS é ter duas APIs, uma para fazer consultas, e outra para comandar o sistema.

Acidentalmente (pesquisando sobre Groovy), descobri que isso está na moda:

Many microservices implementations will move away from their
REST/JSON beginnings and truly embrace event-driven architectures,
with the hardest problems being solved by Event Sourcing and CQRS
implementations.

Tradução:

Muitas implementações de microserviços se afastarão de seus primórdios
REST/JSON e verdadeiramente abraçarão arquiteturas orientadas a
eventos, com os problemas mais difíceis sendo resolvidos por
implementações de Event Sourcing e CQRS.

Qual a relevância da minha observação?

Resposta: Event Sourcing

O exemplo em C# sobre CQRS que você encontra ali no link do MSDN, parece bem orientado a Event Sourcing. Normalmente, elas são implementadas em conjunto, CQRS e Event Sourcing.

The CQRS pattern is often used in conjunction with the Event Sourcing pattern.

A razão disso não é pertinente neste momento (se por acaso estiver curioso, faça outra pergunta no StackOverflow). O problema é que Event Sourcing complica bastante a implementação do CQRS.
A própria documentação fala sobre disso:

The inherent complexity of the CQRS pattern when used in conjunction
with Event Sourcing can make a successful implementation more
difficult

Agora, você quer saber como implementar CQRS, com ou sem Event Sourcing?

Fonte

Related Posts:

Qual a diferença entre AppCompatActivity e Activity? – android android-activity
Pergunta: Qual a diferença da AppCompatActivity para Activity ? A partir de qual versão a AppCompatActivity foi adicionada ao Android? Autor da pergunta Luhhh A diferença reside ...
Como abreviar palavras em PHP? – php string
Pergunta: Possuo informações comuns como nome de pessoas e endereços, e preciso que elas contenham no máximo 30 caracteres sem cortar palavras. Exemplo: 'Avenida Natalino João Brescansin' ...
Qual é a finalidade de um parêntese vazio numa declaração Lambda? – c# expressões-lambda característica-linguagem
Pergunta: Criei um exemplo de uma declaração Lambda sem argumentos, entretanto, estou com duvidas referente a omissão do parêntese vazio () na declaração. Veja o exemplo: class ...
Boas práticas para URI em API RESTful – api rest restful
Pergunta: Estou com dúvida em relação às URIs de alguns recursos da api que estou desenvolvendo. Tenho os recursos projetos e atividades com relação 1-N, ...
Dúvidas sobre a integração do MySQL com Java – java mysql netbeans
Pergunta: Estou criando um sistema no NetBeans, utilizando a linguagem Java e o banco de dados MySQL. Escrevi o seguinte código para realizar a conexão ...
Qual é a finalidade da pasta Model do framework Inphinit? – php inphinit
Pergunta: No Inphinit micro-framework existe a pasta Model que fica dentro da pasta application, e nela é onde ficam as classes, mas eu estou muito ...
Uso do ‘@’ em variáveis – javascript typescript coffeescript
Pergunta: Vejo em algumas linguagens que compilam para javascript, como TypeScript e CoffeeScript, o uso do @ em variáveis, como também, casos em que o ...
Qual tamanho máximo um arquivo JSON pode ter? – json arquivo
Pergunta: Vou dar um exemplo para conseguir explicar minha duvida: Preciso recuperar informação de imagens vindas de uma API, esse banco de imagens me retorna JSON's ...
O que é Teste de Regressão? – terminologia engenharia-de-software testes
Pergunta: Na matéria de Teste de Software o professor abordou um termo chamado Teste de Regressão, isto dentro da disciplina de teste de software. Sendo ...
O que é um construtor da linguagem? – php característica-linguagem
Pergunta: Em PHP, já li e ouvi várias vezes a respeito dos Construtores da Linguagem. Os casos que sempre ouvi falar deles foi em casos ...
Função intrínseca para converter numérico para string – cobol
Pergunta: Estou a tentar saber se existe alguma função intrínseca do COBOL para converter um data numérico para string sem precisar usar a cláusula REDEFINES: ( ...
Porque usar implements? – java android
Pergunta: Qual a diferença entre usar btn.setOnClickListener(new OnClickListener() { e public class MainActivity extends Activity implements OnClickListener{ Estive fazendo um curso de Android e meu professor falou que ...
O que é XHTML e quando deve ser usado? – html xml xhtml
Pergunta: O que eu sei é que o XHTML precisa ser XML válido. Isso implica, por exemplo, que todas as tags precisam ser fechadas. Por ...
Uma placa aceleradora de vídeo pode melhorar o desempenho não-gráfico? [fechada] – desempenho
Pergunta: Para desenvolver em Ruby on Rails, eu utilizo aqui uma máquina virtual do VirtualBox com Ubuntu Server 14.04 sem interface gráfica instalada. Recentemente descobri uma ...
Concat() VS Union() – c# .net
Pergunta: Qual a diferença entre Concat() e Union() ? Quando usar Concat() e quando usar Union() ? Somente pode ser usado em list ? ...

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *