六、深入 CQRS

如果你打算构建大型应用,分离数据源的读写操作可以帮助应用在未来快速扩展。 命令和查询职责隔离(CQRS)模式将帮助您编写可维护和可测试的代码。 对于如何分离所有读取请求和所有修改数据请求,该模式也是理想的候选模式。

这一章是关于 CQRS 模式、中介模式和流行的用于 CQRS 和管道行为的 MediatR NuGet 包。

我们将涵盖以下主题:

  • CQRS 是什么?
  • 什么是中介模式?
  • MediatR 包是什么?
  • 为什么学习 CQRS ?
  • 何时使用 CQRS
  • CQRS 的缺点

技术要求

你将需要 Visual Studio 2019, Visual Studio for Mac,或 Rider 来完成本章。

什么是 CQRS?

CQRS表示命令与查询职责隔离。 这意味着命令和查询应该有单独的职责和明确的域边界。

让说你有一个控制器,控制器有几个端点获取数据,建立数据,更新数据和删除数据,这些都是你的,,,和删除方法简而言之。****

控制器中获取数据或未突变数据的一切都属于查询; 而其他所有改变数据的请求,如 POST、PUT 和 DELETE 请求,则被归类为Command

现在,您的应用应该有一个查询模型来处理从数据库获取数据的查询。 它还应该有一个命令模型,用于处理在数据库中写入或删除数据的命令。

下面是应用中的命令图:

Figure 6.1 – An app sending an HTTP request

图 6.1 -应用发送 HTTP 请求

前面的图 6.1显示 UI 通过命令处理程序向 API 端点发送 POST/UPDATE/DELETE 请求。 在命令处理程序之后,请求转到域模型和基础设施以修改数据库。

查询和命令的分离就是我们所追求的分离。 然而,中介(一种模式)将帮助我们解耦一切。

现在,让我们看另一个例子,我们可以看到一个控制器:

Figure 6.2 – A controller sending a command

图 6.2 -控制器发送命令

图 6.2中,最终的目标是带来一个 GET 请求,并有一个 post,就像在前面的图中一样,最终在中介中结束。 这个中介将找到一个查询处理程序和一个命令处理程序来处理请求,通过这个方法,控制器将不需要知道是否有任何服务被注入到处理程序中。

设计使应用更易于管理,而且更易于测试,因为您可以对独立于整个控制器的处理程序进行单元测试。 毕竟,控制器中什么都没有。 控制器只向中介发送一个查询,并最终到达处理程序中,但这是一种非常解耦的方法,对您的应用具有显著的可预测性。

这就是 CQRS。 现在,让我们转到下一节,了解中介模式是什么。

中介模式是什么?

假设您的应用中有四个服务、对象或元素,这些服务、对象或元素通常需要相互通信。 ServiceA需要跟ServiceB服务,【显示】和ServiceC 需要跟ServiceB服务【病人】,在ServiceB还需要跟【t16.1】提供服务。 这些服务、对象和元素现在紧密耦合在一起。**

调解人来拯救你了。 我们将在中间放置一个中介,而不是服务、对象或元素相互调用。 调解员就像一个机场交通控制塔,它知道我们希望我们的服务如何相互通信,如下图所示:

Figure 6.3 – Mediator as a traffic control tower

图 6.3 -调解员扮演交通管制塔

图 6.3图 6.3中的中介器是处理服务、对象或元素(本例中为飞机)之间通信的交通控制塔。

那么在 ASP 中如何实现中介模式呢? NET Core? 让我们在下一节中找到答案。

MediatR 包是什么?

那么,NuGet MediatR 包做什么呢? MediatR 包是. net 中中介模式的一个实现,可以随时使用。

MediatR 包使用起来并不复杂。 你只需要记住一个请求有一个相应的处理程序,它返回一个响应,如下图所示:

Figure 6.4 – MediatR is an implementation of the mediator pattern

图 6.4 - MediatR 是中介模式的一个实现

您可以在图 6.4 中看到,您可以有多个请求。 一个请求进入 MediatR 包,后面跟着另一个请求。 中间有中介,它接受请求并找到需要处理这些请求的适当处理程序。 每个请求都有一个处理程序; 处理程序完成所有的工作,并为正确的请求返回正确的响应。

MediatR 包就像一个魔盒,您在其中推送请求,但不知道下游将由谁来处理请求。 现在处理请求成为 MediatoR 的职责。 因此,使用 MediatR 的一个原因是它将控制器从应用和业务逻辑完全解耦。

这就是 MediatR 包,一个可以用于 CQRS 的包。 为什么要学习 CQRS? 让我们在下一节中找到答案。

为什么要学习 CQRS?

所有控制器都只有一个依赖项,即 MediatR 包。 你的每个动作都会调用一个方法Mediator.send,并返回一个结果,这使得你的控制器比不使用 CQRS 模式的控制器更精简。

那么什么时候使用 CQRS 呢? 让我们看看下一节。

什么时候使用 CQRS

为什么要使用中介或 CQRS 模式? 原因有很多,但我们只讨论一些明显的原因,如下:

  • 服务相互调用:所有读和写请求进入中间的盒子(中介),然后出来。 如果您想从任何地方触发任何请求,请求必须击中中间的方框,即中介。
  • 大型项目中的干净代码:中介和中介管道的使用将帮助您缩小控制器大小并将业务逻辑移动到它们各自的文件中。 因此,轻松地遍历文件夹结构并找到要查找的逻辑。
  • A boundary between writes and reads: Any UI team can efficiently implement a UI that requires more data from the database due to the separation of reads and writes.

    UI 团队可以自由地工作,而不必担心影响后端工作,以及逻辑如何将通知分发或写入其他通道以触发进一步的服务。

  • 代码的可移植性:中介可以触发您在单独项目中重用的服务。 您可以通过引入中介并导入带有服务的项目来实现这一点,并查看可以通过此框访问您的服务。

这些都是使用 CQRS 的明显原因; 下一个问题是使用 CQRS 是否有任何缺点。 当然,在大多数情况下,利也有弊。 缺点将在下一节中介绍。

CQRS 的缺点

如果您将 CQRS 与存储库模式结合起来,CQRS 将带来额外的代码。 我的建议是不要添加另一个抽象,比如存储库模式,原因如下:

  • 实体框架已经实现了存储库模式。
  • 有 99%的可能性,你不会改变你的实体框架实现与 NHibernate ORM 或 Dapper Micro ORM。

好的,那么我们刚刚完成了 CQRS、Mediator 和 MediatR 包是什么。 让我们总结一下所学内容。

总结

您已经了解了 CQRS 将命令(更改或写入数据的请求)和查询(读取数据的请求)分离开来。 您还了解了 CQRS 如何帮助您编写更苗条的控制器。

您还了解了 Mediator 设计模式的作用类似于命令/查询和处理程序之间的空中交通控制器。 您学习了可以在。net 中使用的中介模式的实现,即 MediatR NuGet 包,它节省了时间,因为您不需要自己实现它,并且使用 mediator 可以使您的代码更简洁、更易于维护。

您还看到了使用 CQRS 的缺点,包括编写额外的代码,但是额外的代码意味着实现 CQRS 以使代码更清晰、更易于维护。

在下一章中,我们将应用 CQRS 模式和中介模式,并在 ASP 中使用 MediatR NuGet 包.NET Core 5 构建一个高度可伸缩和可维护的 Web API。**