Oi,
Obrigado por escrever este artigo sobre CQRS. Estou tentando entender como é a implementação e examinando os exemplos. O código de repositório de amostra fornecido no exemplo é muito semelhante a como normalmente se escreveria um repo, exceto pela convenção de nomenclatura. Por exemplo, acho que o repositório dado abaixo é o mesmo que ProductsCommandHandler
e provavelmente uma diferença seria que não há GetProduct
aqui que normalmente adicionaríamos. Você poderia explicar o que não estou conseguindo aqui?
public class ProductRepository {
void AddNewProduct(Product newProduct) {
...
}
void RateProduct(int productId, int userId, int rating) {
var product = repository.Find(productId);
if (product != null)
{
product.RateProduct(userId, rating);
repository.Save(product);
}
}
}
⚠ Não edite esta seção.
@martinmthomas Obrigado pela sua pergunta! Iremos revisar e fornecer uma atualização conforme apropriado.
@MikeWasson alguma opinião aqui?
AB # 160217 - Obrigado por relatar - este problema está em revisão
@martinmthomas Na verdade, o manipulador de comandos não é um repositório. Ele usa um repositório.
O manipulador de comandos destina-se a "processar os comandos reais, se adequado". No exemplo, a classe ProductsCommandHandler
recebe um IRepository<Product>
em seu construtor.
Digamos que o usuário avalie o produto 5555 com 4 estrelas.
{"product":"5555","stars":4}
RateProduct
e preenche o produto 5555, com estrelas 4. No exemplo, ele também preenche quem está avaliando.CommandHandler
em seu controlador (provavelmente por injeção de dependência) e apenas coloca o comando lá: h.Handle (c); onde c é o comando RateProduct.Portanto, 3 opções: a) Pegue o manipulador, b) Pegue a fila, c) Pegue um ônibus e deixe-o decidir enviar o comando para o manipulador ou para a fila.
Seja qual for a abordagem que você fizer ... a principal diferença sobre "um repositório clássico" é que você NUNCA invoca o próprio repositório no controlador. O controlador não "sabe como gerenciar entidades" (repositório clássico), mas sabe "como gerenciar INTENÇÕES DE FAZER COISAS para entidades" (manipulador de comando).
Então é o manipulador de comandos que -como o nome diz- ele "lida com o comando" que veio de algum lugar (um controlador da web html, um controlador de API, uma linha de comando, qualquer coisa) que foi enviado como um intent e é o CommandHandler (que pertence ao lado da gravação) que decide o que fazer com aquele comando.
Por exemplo, o CommandHandler pode usar um repo para obter o produto, definir o estado e salvar (como no exemplo), mas também pode gravar em um log de eventos, acionar elementos para atualizar os lados de leitura, acionar conectores externos ou qualquer outra coisa.
A IU baseada em tarefas do usuário para classificar o produto e seu controlador como AGNÓSTICO de "onde" o sistema de estrelas / classificação é colocado ou armazenado. Imagine que você projeta um sistema do zero e a classe Product já contém o método RateProduct () como no exemplo. Bom.
Mas ... e se você tiver um sistema legado com uma abordagem "Antiga" para o Produto. No "modelo" (ou seja: Business Mind) não há "classificação" do produto. Em vez disso, o cara de marketing quer "adicionar" classificação ao produto existente, mas toda a empresa concorda que isso é uma "coisa externa". Você usaria o repositório do produto? Ou talvez outro armazenamento auxiliar para que você não "toque" no Produto e no Repositório de Produtos já testados e em funcionamento?
Se você usar comandos, isso não importa para o controlador de gravação. A web, a API e a CLI irão apenas enviar o "comando" para o manipulador de comandos (seja diretamente, via fila ou por meio de um barramento de comando que irá rotear por sua vez para o manipulador ou para uma fila) e eles esquecerão. Em seguida, o manipulador de comandos decidirá o que fazer com o "RateProductCommand" em um ponto centralizado e pequeno de seu código-fonte, isso desvinculando a forma como isso é tratado do código da aplicação, ganhando assim capacidade de manutenção.
Em seguida, o manipulador decidirá se é adequado usar o ProductRepository ou qualquer outra abordagem para armazenar "a classificação de um produto".
Então, para responder:
CommandHandler => não tem nada a ver com os direitos. Ele lida com as "intenções do usuário" (que, em última análise, podem ser alterar entidades; portanto, muito provavelmente o manipulador de comandos usa um repositório).
Repositório => o armazenamento real para uma determinada entidade.
Espero ajudar.
Xavi.
Como @xmontero mencionou, ProductsCommandHandler e ProductRepository tem apenas relação "tem". ProductsCommandHandler atua como interface entre ProductApi / Controller e ProductRepository. Essa implementação ajuda a manter os comandos juntos separados das consultas. Na abordagem tradicional, usamos diretamente o repositório do controlador, mantendo a execução de comandos e consultas juntos, portanto, não há necessidade de qualquer outra interface entre eles.
Você aplicou o padrão de design CQRS em seu aplicativo, se mantiver os Comandos separados das Consultas. Leva você na direção de obter todos os benefícios do CQRS mencionados nos artigos.
Comentários muito úteis
@martinmthomas Na verdade, o manipulador de comandos não é um repositório. Ele usa um repositório.
O manipulador de comandos destina-se a "processar os comandos reais, se adequado". No exemplo, a classe
ProductsCommandHandler
recebe umIRepository<Product>
em seu construtor.Digamos que o usuário avalie o produto 5555 com 4 estrelas.
{"product":"5555","stars":4}
RateProduct
e preenche o produto 5555, com estrelas 4. No exemplo, ele também preenche quem está avaliando.CommandHandler
em seu controlador (provavelmente por injeção de dependência) e apenas coloca o comando lá: h.Handle (c); onde c é o comando RateProduct.Portanto, 3 opções: a) Pegue o manipulador, b) Pegue a fila, c) Pegue um ônibus e deixe-o decidir enviar o comando para o manipulador ou para a fila.
Seja qual for a abordagem que você fizer ... a principal diferença sobre "um repositório clássico" é que você NUNCA invoca o próprio repositório no controlador. O controlador não "sabe como gerenciar entidades" (repositório clássico), mas sabe "como gerenciar INTENÇÕES DE FAZER COISAS para entidades" (manipulador de comando).
Então é o manipulador de comandos que -como o nome diz- ele "lida com o comando" que veio de algum lugar (um controlador da web html, um controlador de API, uma linha de comando, qualquer coisa) que foi enviado como um intent e é o CommandHandler (que pertence ao lado da gravação) que decide o que fazer com aquele comando.
Por exemplo, o CommandHandler pode usar um repo para obter o produto, definir o estado e salvar (como no exemplo), mas também pode gravar em um log de eventos, acionar elementos para atualizar os lados de leitura, acionar conectores externos ou qualquer outra coisa.
A IU baseada em tarefas do usuário para classificar o produto e seu controlador como AGNÓSTICO de "onde" o sistema de estrelas / classificação é colocado ou armazenado. Imagine que você projeta um sistema do zero e a classe Product já contém o método RateProduct () como no exemplo. Bom.
Mas ... e se você tiver um sistema legado com uma abordagem "Antiga" para o Produto. No "modelo" (ou seja: Business Mind) não há "classificação" do produto. Em vez disso, o cara de marketing quer "adicionar" classificação ao produto existente, mas toda a empresa concorda que isso é uma "coisa externa". Você usaria o repositório do produto? Ou talvez outro armazenamento auxiliar para que você não "toque" no Produto e no Repositório de Produtos já testados e em funcionamento?
Se você usar comandos, isso não importa para o controlador de gravação. A web, a API e a CLI irão apenas enviar o "comando" para o manipulador de comandos (seja diretamente, via fila ou por meio de um barramento de comando que irá rotear por sua vez para o manipulador ou para uma fila) e eles esquecerão. Em seguida, o manipulador de comandos decidirá o que fazer com o "RateProductCommand" em um ponto centralizado e pequeno de seu código-fonte, isso desvinculando a forma como isso é tratado do código da aplicação, ganhando assim capacidade de manutenção.
Em seguida, o manipulador decidirá se é adequado usar o ProductRepository ou qualquer outra abordagem para armazenar "a classificação de um produto".
Então, para responder:
CommandHandler => não tem nada a ver com os direitos. Ele lida com as "intenções do usuário" (que, em última análise, podem ser alterar entidades; portanto, muito provavelmente o manipulador de comandos usa um repositório).
Repositório => o armazenamento real para uma determinada entidade.
Espero ajudar.
Xavi.