Hi,
Thanks for writing this article on CQRS. I am trying to understand how the implementation looks like and I was going through the examples. The sample repository code provided in the example look very similar to how normally one would write a repo except for the naming convention. For example, I think the repository given below is same as ProductsCommandHandler
and probably one difference would be that there is no GetProduct
in here which normally we would add. Could you please explain what I am not getting here?
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);
}
}
}
⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
@martinmthomas Thank you for your question! We will review and provide an update as appropriate.
@MikeWasson any thoughts here?
AB#160217 - Thanks for reporting - this issue is under review
@martinmthomas In fact the command handler is not a repository. It uses a repository.
The command handler is meant to "process the actual commands, if suitable". In the example the ProductsCommandHandler
class receives a IRepository<Product>
in its constructor.
Say the user is going to rate product 5555 to 4 stars.
{"product":"5555","stars":4}
RateProduct
and fills in product 5555, stars 4. In the example it also fills in who is rating.CommandHandler
in your controller (probably by dependency injection) and just place the command there: h.Handle( c ); where c is the RateProduct command.So 3 options: a) Get the handler, b) Get the queue, c) Get a bus and let it decide to send the command to the handler or to the queue.
Whatever approach you do... the main difference about "a classical repository" is that you are NEVER invoking the repository itself in the controller. The controller does not "know about how to manage entities" (classical repository) but does know "how to manage INTENTS OF DOING THINGS to entities" (command handler).
Then it is the command handler that -as its name says- it "handles the command" that came from somewhere (an html web controller, an API controller, a command line, whatever) that was sent as an intent and it is the CommandHandler (which belongs to the write side) that decides what to do with that command.
For example, the CommandHandler can use a repo to get the product, set the state and save (as in the example), but it also could write to an event log, trigger elements to update read-sides, trigger external connectors or whatever.
The task-based UI of the user to rate the product and its controller to be AGNOSTIC of "where" the starring/rating system is placed or stored. Imagine you design a system from scratch and the Product class already contains the RateProduct() method like in the example. Good.
But... what if you have a legacy system there with an "Old" approach to the Product. In the "model" (ie: Business Mind) there's no "rating" of the product. Instead the marketing guy wants to "add" rating to the existing product but all the company agrees that this is an "external thing". Would you use the Product repository? Or maybe another helper storage so you don't "touch" the alread-yworking-and-fully-tested Product and ProductRepository?
If you use commands, it does not mind to the writing controller. The web, the API and the CLI will all just send the "command" to the command handler (either directly, via queue or via a command bus that will route in turn to the handler or to a queue) and they forget. Then the command handler will decide what to do with the "RateProductCommand" in a centralized and small point of your source code, this decoupling the way this is handled from the application code, thus gaining mantainability.
Then the handler will decide if it's suitable to use the ProductRepository or whatever other approach to store "the rating of a product".
So, to answer:
CommandHandler => has nothing to do with the entitites. It handles the "intentions of the user" (that ultimately could be to alter entities; so most probably the command handler uses a repository).
Repository => the actual storage for a certain entity.
Hope to help.
Xavi.
As @xmontero mentioned, ProductsCommandHandler and ProductRepository has only "has" relation. ProductsCommandHandler acts as interface between ProductApi/Controller and ProductRepository. This implementation helps keep commands together separate from Queries. In traditional approach, we directly use repository from controller keeping executing commands and Queries together hence no need of any other interface between.
You have applied CQRS design pattern in your application, if you keep Commands separated from Queries. It takes you in the direction of getting all the benefits of CQRS mentioned in the articles.
Most helpful comment
@martinmthomas In fact the command handler is not a repository. It uses a repository.
The command handler is meant to "process the actual commands, if suitable". In the example the
ProductsCommandHandler
class receives aIRepository<Product>
in its constructor.Say the user is going to rate product 5555 to 4 stars.
{"product":"5555","stars":4}
RateProduct
and fills in product 5555, stars 4. In the example it also fills in who is rating.CommandHandler
in your controller (probably by dependency injection) and just place the command there: h.Handle( c ); where c is the RateProduct command.So 3 options: a) Get the handler, b) Get the queue, c) Get a bus and let it decide to send the command to the handler or to the queue.
Whatever approach you do... the main difference about "a classical repository" is that you are NEVER invoking the repository itself in the controller. The controller does not "know about how to manage entities" (classical repository) but does know "how to manage INTENTS OF DOING THINGS to entities" (command handler).
Then it is the command handler that -as its name says- it "handles the command" that came from somewhere (an html web controller, an API controller, a command line, whatever) that was sent as an intent and it is the CommandHandler (which belongs to the write side) that decides what to do with that command.
For example, the CommandHandler can use a repo to get the product, set the state and save (as in the example), but it also could write to an event log, trigger elements to update read-sides, trigger external connectors or whatever.
The task-based UI of the user to rate the product and its controller to be AGNOSTIC of "where" the starring/rating system is placed or stored. Imagine you design a system from scratch and the Product class already contains the RateProduct() method like in the example. Good.
But... what if you have a legacy system there with an "Old" approach to the Product. In the "model" (ie: Business Mind) there's no "rating" of the product. Instead the marketing guy wants to "add" rating to the existing product but all the company agrees that this is an "external thing". Would you use the Product repository? Or maybe another helper storage so you don't "touch" the alread-yworking-and-fully-tested Product and ProductRepository?
If you use commands, it does not mind to the writing controller. The web, the API and the CLI will all just send the "command" to the command handler (either directly, via queue or via a command bus that will route in turn to the handler or to a queue) and they forget. Then the command handler will decide what to do with the "RateProductCommand" in a centralized and small point of your source code, this decoupling the way this is handled from the application code, thus gaining mantainability.
Then the handler will decide if it's suitable to use the ProductRepository or whatever other approach to store "the rating of a product".
So, to answer:
CommandHandler => has nothing to do with the entitites. It handles the "intentions of the user" (that ultimately could be to alter entities; so most probably the command handler uses a repository).
Repository => the actual storage for a certain entity.
Hope to help.
Xavi.