Salut,
Merci d'avoir écrit cet article sur le CQRS. J'essaie de comprendre à quoi ressemble la mise en œuvre et je passais en revue les exemples. L'exemple de code de référentiel fourni dans l'exemple ressemble beaucoup à la façon dont on écrirait normalement un dépôt, à l'exception de la convention de dénomination. Par exemple, je pense que le référentiel donné ci-dessous est le même que ProductsCommandHandler
et probablement une différence serait qu'il n'y a pas de GetProduct
ici que nous ajouterions normalement. Pourriez-vous s'il vous plaît expliquer ce que je n'obtiens pas ici?
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);
}
}
}
⚠ Ne modifiez pas cette section.
@martinmthomas Merci pour votre question! Nous examinerons et fournirons une mise à jour le cas échéant.
@MikeWasson des pensées ici?
AB # 160217 - Merci d'avoir signalé ce problème - ce problème est en cours d'examen
@martinmthomas En fait, le gestionnaire de commandes n'est pas un référentiel. Il utilise un référentiel.
Le gestionnaire de commandes est censé "traiter les commandes réelles, si cela est approprié". Dans l'exemple, la classe ProductsCommandHandler
reçoit un IRepository<Product>
dans son constructeur.
Supposons que l'utilisateur attribue au produit 5555 une note de 4 étoiles.
{"product":"5555","stars":4}
RateProduct
et remplit le produit 5555, étoiles 4. Dans l'exemple, il remplit également qui évalue.CommandHandler
dans votre contrôleur (probablement par injection de dépendance) et placez simplement la commande là: h.Handle (c); où c est la commande RateProduct.Donc 3 options: a) Récupérez le gestionnaire, b) Récupérez la file d'attente, c) Obtenez un bus et laissez-le décider d'envoyer la commande au gestionnaire ou à la file d'attente.
Quelle que soit l'approche que vous faites ... la principale différence avec "un référentiel classique" est que vous n'invoquez JAMAIS le référentiel lui-même dans le contrôleur. Le contrôleur ne "sait pas comment gérer les entités" (référentiel classique) mais sait "comment gérer les INTENTS OF FAIRE CHOSES aux entités" (gestionnaire de commandes).
Ensuite, c'est le gestionnaire de commandes qui -comme son nom l'indique- il "gère la commande" qui vient de quelque part (un contrôleur web html, un contrôleur API, une ligne de commande, peu importe) qui a été envoyé comme une intention et c'est le CommandHandler (qui appartient au côté écriture) qui décide quoi faire avec cette commande.
Par exemple, le CommandHandler peut utiliser un référentiel pour obtenir le produit, définir l'état et enregistrer (comme dans l'exemple), mais il peut également écrire dans un journal des événements, déclencher des éléments pour mettre à jour les côtés de lecture, déclencher des connecteurs externes ou autre.
L'interface utilisateur basée sur les tâches de l'utilisateur pour évaluer le produit et son contrôleur comme étant AGNOSTIQUE de «l'endroit» où le système de mise en vedette / d'évaluation est placé ou stocké. Imaginez que vous concevez un système à partir de zéro et que la classe Product contient déjà la méthode RateProduct () comme dans l'exemple. Bien.
Mais ... que se passe-t-il si vous avez un ancien système avec une approche «ancienne» du produit. Dans le «modèle» (c'est-à-dire: Business Mind), il n'y a pas de «note» du produit. Au lieu de cela, le responsable du marketing veut «ajouter» une note au produit existant, mais toute l'entreprise convient qu'il s'agit d'une «chose externe». Souhaitez-vous utiliser le référentiel de produits? Ou peut-être un autre stockage d'aide pour ne pas «toucher» le produit et le référentiel de produits déjà en cours de fonctionnement et entièrement testés?
Si vous utilisez des commandes, cela ne dérange pas le contrôleur d'écriture. Le web, l'API et la CLI enverront tous simplement la "commande" au gestionnaire de commandes (soit directement, via la file d'attente ou via un bus de commande qui acheminera à son tour vers le gestionnaire ou vers une file d'attente) et ils oublient. Ensuite, le gestionnaire de commandes décidera quoi faire avec le "RateProductCommand" dans un point centralisé et petit de votre code source, ce découplant la façon dont cela est géré du code de l'application, gagnant ainsi en maintenabilité.
Ensuite, le gestionnaire décidera s'il est approprié d'utiliser le ProductRepository ou toute autre approche pour stocker "l'évaluation d'un produit".
Alors, pour répondre:
CommandHandler => n'a rien à voir avec les droits. Il gère les «intentions de l'utilisateur» (ce qui pourrait finalement être de modifier des entités; donc très probablement le gestionnaire de commandes utilise un référentiel).
Repository => le stockage réel pour une certaine entité.
J'espère aider.
Xavi.
Comme @xmontero l'a mentionné, ProductsCommandHandler et ProductRepository n'ont qu'une relation «a». ProductsCommandHandler agit comme interface entre ProductApi / Controller et ProductRepository. Cette implémentation permet de séparer les commandes des requêtes. Dans l'approche traditionnelle, nous utilisons directement le référentiel du contrôleur en maintenant l'exécution des commandes et des requêtes ensemble, donc pas besoin d'une autre interface entre les deux.
Vous avez appliqué le modèle de conception CQRS dans votre application, si vous gardez les commandes séparées des requêtes. Cela vous amène à profiter de tous les avantages du CQRS mentionnés dans les articles.
Commentaire le plus utile
@martinmthomas En fait, le gestionnaire de commandes n'est pas un référentiel. Il utilise un référentiel.
Le gestionnaire de commandes est censé "traiter les commandes réelles, si cela est approprié". Dans l'exemple, la classe
ProductsCommandHandler
reçoit unIRepository<Product>
dans son constructeur.Supposons que l'utilisateur attribue au produit 5555 une note de 4 étoiles.
{"product":"5555","stars":4}
RateProduct
et remplit le produit 5555, étoiles 4. Dans l'exemple, il remplit également qui évalue.CommandHandler
dans votre contrôleur (probablement par injection de dépendance) et placez simplement la commande là: h.Handle (c); où c est la commande RateProduct.Donc 3 options: a) Récupérez le gestionnaire, b) Récupérez la file d'attente, c) Obtenez un bus et laissez-le décider d'envoyer la commande au gestionnaire ou à la file d'attente.
Quelle que soit l'approche que vous faites ... la principale différence avec "un référentiel classique" est que vous n'invoquez JAMAIS le référentiel lui-même dans le contrôleur. Le contrôleur ne "sait pas comment gérer les entités" (référentiel classique) mais sait "comment gérer les INTENTS OF FAIRE CHOSES aux entités" (gestionnaire de commandes).
Ensuite, c'est le gestionnaire de commandes qui -comme son nom l'indique- il "gère la commande" qui vient de quelque part (un contrôleur web html, un contrôleur API, une ligne de commande, peu importe) qui a été envoyé comme une intention et c'est le CommandHandler (qui appartient au côté écriture) qui décide quoi faire avec cette commande.
Par exemple, le CommandHandler peut utiliser un référentiel pour obtenir le produit, définir l'état et enregistrer (comme dans l'exemple), mais il peut également écrire dans un journal des événements, déclencher des éléments pour mettre à jour les côtés de lecture, déclencher des connecteurs externes ou autre.
L'interface utilisateur basée sur les tâches de l'utilisateur pour évaluer le produit et son contrôleur comme étant AGNOSTIQUE de «l'endroit» où le système de mise en vedette / d'évaluation est placé ou stocké. Imaginez que vous concevez un système à partir de zéro et que la classe Product contient déjà la méthode RateProduct () comme dans l'exemple. Bien.
Mais ... que se passe-t-il si vous avez un ancien système avec une approche «ancienne» du produit. Dans le «modèle» (c'est-à-dire: Business Mind), il n'y a pas de «note» du produit. Au lieu de cela, le responsable du marketing veut «ajouter» une note au produit existant, mais toute l'entreprise convient qu'il s'agit d'une «chose externe». Souhaitez-vous utiliser le référentiel de produits? Ou peut-être un autre stockage d'aide pour ne pas «toucher» le produit et le référentiel de produits déjà en cours de fonctionnement et entièrement testés?
Si vous utilisez des commandes, cela ne dérange pas le contrôleur d'écriture. Le web, l'API et la CLI enverront tous simplement la "commande" au gestionnaire de commandes (soit directement, via la file d'attente ou via un bus de commande qui acheminera à son tour vers le gestionnaire ou vers une file d'attente) et ils oublient. Ensuite, le gestionnaire de commandes décidera quoi faire avec le "RateProductCommand" dans un point centralisé et petit de votre code source, ce découplant la façon dont cela est géré du code de l'application, gagnant ainsi en maintenabilité.
Ensuite, le gestionnaire décidera s'il est approprié d'utiliser le ProductRepository ou toute autre approche pour stocker "l'évaluation d'un produit".
Alors, pour répondre:
CommandHandler => n'a rien à voir avec les droits. Il gère les «intentions de l'utilisateur» (ce qui pourrait finalement être de modifier des entités; donc très probablement le gestionnaire de commandes utilise un référentiel).
Repository => le stockage réel pour une certaine entité.
J'espère aider.
Xavi.