Architecture-center: ProductsCommandHandler(CQRS)和ProductRepository(传统架构)之间有什么区别?

创建于 2019-11-15  ·  5评论  ·  资料来源: MicrosoftDocs/architecture-center

你好,
感谢您在CQRS上撰写本文。 我正在尝试了解实现的外观,并且正在研究示例。 该示例中提供的示例存储库代码看起来与除命名约定外通常如何编写存储库相似。 例如,我认为下面给出的存储库与ProductsCommandHandler ,可能的不同之处在于这里通常没有我们要添加的GetProduct 。 你能解释一下我没到这里吗?

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

文件详细资料

请勿编辑此部分。

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

最有用的评论

@martinmthomas实际上,命令处理程序不是存储库。 它使用一个存储库。

命令处理程序旨在“在适当的情况下处理实际命令”。 在示例中, ProductsCommandHandler类在其构造函数中收到IRepository<Product>

假设用户将产品5555评分为4星。

  • 用户看到一个界面。 假设一个网页(可以是本地.exe程序或其他任何东西,让我们想象一个网站)。
  • 用户单击一个按钮。 假设这会触发AJAX POST转到数据{"product":"5555","stars":4}的路由/ rate
  • POST路由具有一个控制器。 该控制器与POST调用中的“写”操作相关联。
  • 这里的控制器采用经典方法,只需加载经典产品存储库并加载产品5555。然后告诉产品“您现在被评为4星”并保存。
  • 在这种方法中,控制器构建命令RateProduct并填写产品5555(星号4)。在该示例中,它还填写谁进行评分。
  • 此时,控制器“发送命令”到写入模型。 您在此处有2个选项:调用命令处理程序或将其放入队列。
  • 假设您没有队列。 然后,在控制器中获得CommandHandler (可能是通过依赖注入),然后将命令放在此处:h.Handle(c); 其中c是RateProduct命令。
  • 假设您有一个队列。 然后,您可以在控制器中获得队列(可能是通过依赖注入),并在此将命令排队:q.Queue(c);
  • 在这最后一种情况下,队列的侦听器将命令从队列中移出,而是调用命令处理程序。
  • 作为首选方法(建议),控制器不选择命令是同步处理(获取处理程序)还是异步处理(获取队列),而应使用“通用方法”,即在命令总线上使用命令总线放在。 假设命令总线为b,那么您将执行b.Send(c); 其中c是RateProduct命令。
  • 通过这种第三种方法,您可以将命令处理程序放置在总线的末端,并且可以将中间件配置为“从总线上取走命令并将其放置在队列中”。

所以有3个选择:a)获取处理程序,b)获取队列,c)获取总线,并让它决定将命令发送到处理程序还是队列。

无论采用哪种方法,“经典存储库”的主要区别在于,您永远不会在控制器中调用存储库本身。 控制器不“知道如何管理实体”(经典存储库),但确实知道“如何管理对实体做事的意图”(命令处理程序)。

然后是命令处理程序-顾名思义-它“处理命令”来自意图发送的某处(html Web控制器,API控制器,命令行等),它是CommandHandler (属于写端),它决定如何处理该命令。

例如,CommandHandler可以使用存储库来获取产品,设置状态并保存(如示例中所示),但是它也可以写入事件日志,触发元素以更新读取侧,触发外部连接器或其他任何东西。

用户基于任务的用户界面将产品及其控制器定级为“放置或存储星标/定级系统”的“位置”。 假设您从头开始设计一个系统,并且Product类已经包含RateProduct()方法,如示例中所示。 好的。

但是...如果您那里有一个采用“旧”产品方法的旧系统,该怎么办。 在“模型”(即Business Mind)中,没有产品的“评级”。 相反,营销人员希望对现有产品进行“添加”评级,但是所有公司都同意这是“外部因素”。 您会使用产品存储库吗? 或者,也许是另一个帮助程序存储,这样您就不会“接触”已经在使用中并且已经过充分测试的产品和产品存储库了吗?

如果使用命令,则不介意编写控制器。 Web,API和CLI都只是将“命令”发送到命令处理程序(直接通过队列或通过将依次路由到处理程序或队列的命令总线)发送到命令处理程序,而他们忘记了。 然后,命令处理程序将在源代码的集中且较小的位置决定如何处理“ RateProductCommand”,这将处理方式与应用程序代码分离,从而获得了可维护性。

然后,处理程序将决定是否适合使用ProductRepository或其他任何方式来存储“产品的评分”。

所以,要回答:

CommandHandler =>与实体无关。 它处理“用户意图”(最终可能是更改实体;因此,命令处理程序很可能使用存储库)。
存储库=>某个实体的实际存储。

希望对您有所帮助。
哈维

所有5条评论

@martinmthomas谢谢您的提问! 我们将审查并提供适当的更新。

@MikeWasson在这里有什么想法吗?

AB#160217-感谢您的举报-此问题正在审核中

@martinmthomas实际上,命令处理程序不是存储库。 它使用一个存储库。

命令处理程序旨在“在适当的情况下处理实际命令”。 在示例中, ProductsCommandHandler类在其构造函数中收到IRepository<Product>

假设用户将产品5555评分为4星。

  • 用户看到一个界面。 假设一个网页(可以是本地.exe程序或其他任何东西,让我们想象一个网站)。
  • 用户单击一个按钮。 假设这会触发AJAX POST转到数据{"product":"5555","stars":4}的路由/ rate
  • POST路由具有一个控制器。 该控制器与POST调用中的“写”操作相关联。
  • 这里的控制器采用经典方法,只需加载经典产品存储库并加载产品5555。然后告诉产品“您现在被评为4星”并保存。
  • 在这种方法中,控制器构建命令RateProduct并填写产品5555(星号4)。在该示例中,它还填写谁进行评分。
  • 此时,控制器“发送命令”到写入模型。 您在此处有2个选项:调用命令处理程序或将其放入队列。
  • 假设您没有队列。 然后,在控制器中获得CommandHandler (可能是通过依赖注入),然后将命令放在此处:h.Handle(c); 其中c是RateProduct命令。
  • 假设您有一个队列。 然后,您可以在控制器中获得队列(可能是通过依赖注入),并在此将命令排队:q.Queue(c);
  • 在这最后一种情况下,队列的侦听器将命令从队列中移出,而是调用命令处理程序。
  • 作为首选方法(建议),控制器不选择命令是同步处理(获取处理程序)还是异步处理(获取队列),而应使用“通用方法”,即在命令总线上使用命令总线放在。 假设命令总线为b,那么您将执行b.Send(c); 其中c是RateProduct命令。
  • 通过这种第三种方法,您可以将命令处理程序放置在总线的末端,并且可以将中间件配置为“从总线上取走命令并将其放置在队列中”。

所以有3个选择:a)获取处理程序,b)获取队列,c)获取总线,并让它决定将命令发送到处理程序还是队列。

无论采用哪种方法,“经典存储库”的主要区别在于,您永远不会在控制器中调用存储库本身。 控制器不“知道如何管理实体”(经典存储库),但确实知道“如何管理对实体做事的意图”(命令处理程序)。

然后是命令处理程序-顾名思义-它“处理命令”来自意图发送的某处(html Web控制器,API控制器,命令行等),它是CommandHandler (属于写端),它决定如何处理该命令。

例如,CommandHandler可以使用存储库来获取产品,设置状态并保存(如示例中所示),但是它也可以写入事件日志,触发元素以更新读取侧,触发外部连接器或其他任何东西。

用户基于任务的用户界面将产品及其控制器定级为“放置或存储星标/定级系统”的“位置”。 假设您从头开始设计一个系统,并且Product类已经包含RateProduct()方法,如示例中所示。 好的。

但是...如果您那里有一个采用“旧”产品方法的旧系统,该怎么办。 在“模型”(即Business Mind)中,没有产品的“评级”。 相反,营销人员希望对现有产品进行“添加”评级,但是所有公司都同意这是“外部因素”。 您会使用产品存储库吗? 或者,也许是另一个帮助程序存储,这样您就不会“接触”已经在使用中并且已经过充分测试的产品和产品存储库了吗?

如果使用命令,则不介意编写控制器。 Web,API和CLI都只是将“命令”发送到命令处理程序(直接通过队列或通过将依次路由到处理程序或队列的命令总线)发送到命令处理程序,而他们忘记了。 然后,命令处理程序将在源代码的集中且较小的位置决定如何处理“ RateProductCommand”,这将处理方式与应用程序代码分离,从而获得了可维护性。

然后,处理程序将决定是否适合使用ProductRepository或其他任何方式来存储“产品的评分”。

所以,要回答:

CommandHandler =>与实体无关。 它处理“用户意图”(最终可能是更改实体;因此,命令处理程序很可能使用存储库)。
存储库=>某个实体的实际存储。

希望对您有所帮助。
哈维

@xmontero所述,ProductsCommandHandler和ProductRepository仅具有“具有”关系。 ProductsCommandHandler充当ProductApi / Controller和ProductRepository之间的接口。 此实现有助于使命令与查询分开保持在一起。 在传统方法中,我们直接使用控制器中的存储库,将执行命令和查询保持在一起,因此之间不需要任何其他接口。

如果您将“命令”与“查询”分开,则已在应用程序中应用了CQRS设计模式。 它使您朝着获得本文中提到的CQRS的所有优势的方向发展。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

marcusturewicz picture marcusturewicz  ·  3评论

JannieSA picture JannieSA  ·  7评论

adrianwells picture adrianwells  ·  5评论

clemensv picture clemensv  ·  10评论

melzayet picture melzayet  ·  4评论