Architecture-center: What is the difference between ProductsCommandHandler (CQRS) and ProductRepository (traditional arch)?

Created on 15 Nov 2019  ·  5Comments  ·  Source: MicrosoftDocs/architecture-center

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);
    }
  }
}

Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Pri1 architecture-centesvc assigned-to-author cloud-fundamentalsubsvc product-question triaged

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 a IRepository<Product> in its constructor.

Say the user is going to rate product 5555 to 4 stars.

  • The user sees an interface. Let's say a web page (could be a local .exe program or whatever, let's imagine a website).
  • The user clicks a button. Let's say this triggers an AJAX POST going to a route /rate with data {"product":"5555","stars":4}
  • The POST route has a controller. This controller is associated to a "write" operation as it comes from a POST call.
  • The controller here in a classical approach would just load the classic product repository and load the product 5555. Then tell the product "you are now rated 4 stars" and save.
  • In this approach, the controller builds the command RateProduct and fills in product 5555, stars 4. In the example it also fills in who is rating.
  • At this moment, the controller "sends the command" to the write model. You have 2 options here: Invoke a command handler or queue it.
  • Say you don't have a queue. You then get the CommandHandler in your controller (probably by dependency injection) and just place the command there: h.Handle( c ); where c is the RateProduct command.
  • Say you have a queue. You then get the queue in your controller (probably by dependency injection) and just enqueue the command there: q.Queue( c );
  • In this last case, the listener of the queue will take the command out of the queue and call the command handler instead.
  • As a thirs option (recommended), your controller does not choose if the command is being processed synchronously (get the handler) or asynchronously (get the queue) but should use a "generic approach" which is to use a command bus where the commands are placed. Say the command bus is b, then you'd do b.Send( c ); where c is the RateProduct command.
  • With this 3rd approach you place the command handler at the end of the bus, and you can configure middlewares to "take off the command from the bus and placing it on the queue".

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.

All 5 comments

@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.

  • The user sees an interface. Let's say a web page (could be a local .exe program or whatever, let's imagine a website).
  • The user clicks a button. Let's say this triggers an AJAX POST going to a route /rate with data {"product":"5555","stars":4}
  • The POST route has a controller. This controller is associated to a "write" operation as it comes from a POST call.
  • The controller here in a classical approach would just load the classic product repository and load the product 5555. Then tell the product "you are now rated 4 stars" and save.
  • In this approach, the controller builds the command RateProduct and fills in product 5555, stars 4. In the example it also fills in who is rating.
  • At this moment, the controller "sends the command" to the write model. You have 2 options here: Invoke a command handler or queue it.
  • Say you don't have a queue. You then get the CommandHandler in your controller (probably by dependency injection) and just place the command there: h.Handle( c ); where c is the RateProduct command.
  • Say you have a queue. You then get the queue in your controller (probably by dependency injection) and just enqueue the command there: q.Queue( c );
  • In this last case, the listener of the queue will take the command out of the queue and call the command handler instead.
  • As a thirs option (recommended), your controller does not choose if the command is being processed synchronously (get the handler) or asynchronously (get the queue) but should use a "generic approach" which is to use a command bus where the commands are placed. Say the command bus is b, then you'd do b.Send( c ); where c is the RateProduct command.
  • With this 3rd approach you place the command handler at the end of the bus, and you can configure middlewares to "take off the command from the bus and placing it on the queue".

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.

Was this page helpful?
0 / 5 - 0 ratings