31

CQRS-简单的架构设计

 5 years ago
source link: https://www.oschina.net/translate/cqrs-simple-architecture?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

有人说:“CQRS很难!”

是吗? 好吧,我也曾这样认为! 但,当我开始使用 CQRS 编写我的第一个软件时,它很快就不攻自破。更为重要的是,我认为从长远来看,以这种方式维护软件更加容易。

我开始思考:为何人们在一开始时认为它是多么困难难和复杂? 我有一个理论:它包含规则! 进入拥有规则的世界总是不舒服的,我们需要适应这些规则。在这篇文章中,我想证明在这种情况下,这些规则是非常易于理解的。

在通往 CQRS 的路上…

从根本上来说,我们可以将 CQRS 视为对软件架构命令查询分离规则的实现。在使用此方法的工作中,我注意到在最简单的 CQS 实现 与真正成熟的 CQRS 之间有几个步骤。我想那些步骤可以顺利地引入我之前已经提到的规则。

虽然第一步没有实现我对 CQRS 的定义(但是有时候这么称呼的),但是他们还可以为你的软件引入一些真正的价值。每个步骤都引入一些有趣的想法,可以有助于构建或清理你的代码库/架构。

通常,我们的旅程从这里开始:

jaAVfyn.jpg!web

我们可能都知道,这是一个典型的 N-层架构。如果我们想在这添加一些 CQS,我们可以“简单地”将业务逻辑层分离为命令和查询:

naAFZ3i.jpg!web

如果你还在使用老式代码库,这可能是最难的一步,就像从意大利面式代码中阅读分离出副作用一样不简单。同时这个步骤可能也是最有好处的一个;它会给你一个副作用执行的位置的概述。

等一下!你正在讨论 CQS,CQRS ,但是你还没有定义到底什么是命令或查询!

没错。我们开始定义它们吧!在这里,我会给你我个人、直观的对命令和查询的定义。它并不全面,而且在实现之前必须加以深化。

命令——首先,触发命令是唯一改变系统状态的方法。命令负责引起 所有 的对系统的改变。如果没有命令,系统状态保持不变!命令不应该返回任何值。我使用两个类来实现它:Command 和 CommandHandler 。Command 只是一个普通的对象,CommandHandler 将它用于表示某些操作的输入值(参数)。我认为命令是简单地调用领域模型中的特定操作(不一定是每个命令都有的操作)。

查询——同样的,查询是一个读操作。它读取系统的状态,过滤,聚总,以及转换数据,并将其转化为最有用的格式。它可以执行多次,而且不会影响系统的状态。我之前是使用一个有一些 Execute(…) 函数的类来实现它,但是现在我认为分离成 Query 和 QueryHandler/QueryExecutor 可能会更有用。

回到示意图,我需要澄清一些事情;我已经隐秘地做了一个补充修改,模型改为领域模型。由于我认为模型是一组数据容器,而领域模型包括了业务规则中本质复杂性。因为我们对这里的体系架构感兴趣,这个修改不会直接影响我们的进一步考虑。但是值得一提的是,尽管命令负责改变系统的状态,本质复杂性应该放到领域模型。

好的,现在我们可以添加新的命令或者编写新的查询。短时间内,很明显,适用于写的领域模型并不一定适合读。从某种特殊模型中更容易读取数据,这并不是一个重大的发现:

UnyMVzn.jpg!web

我们可以引入分离模型,由 ORM 映射并构建查询,但是在某些情况下,特别是当 ORM 引入开销时,它将对简化结构有所帮助。

v2En6rZ.jpg!web

我认为这个特殊的改变应当被好好地考虑!

现在的问题是我们仍然有仅在逻辑层级上分离的读和写模型,因为他们共享公共数据库。这就意味着我们已经分离了读模型,但最有可能是被一些 DB 视图给虚拟化了,物化视图的情况下更好。如果我们的系统没有性能问题,并且我们记住在写模型改变的时候更新查询,那这个方案是可行的。

下一步是引入完全分离的数据模型:

nYRfEnf.jpg!web

在我看来,这是第一个符合 Greg Young 提出的原始想法的模型,现在我们称它为 CQRS 。但是它仍然有问题!我之后再写。

CQRS != 事件溯源

事件溯源是与 CQRS 一起提出的一个概念,通常被标识为 CQRS 的一部分。ES(Event Sourcing)的概念很简单:我们的领域生成的事件表示系统中的每一个更改。如果我们从系统开始记录每一个事件,而且从最初状态开始重现,我们会得到系统的当前状态。它与银行账户的事务相似;我们可以从空账户开始,重现每一个单独的事务,然后(有希望地)得到当前的余款。因此,如果我们已经存储了所有的事件,我们能得到系统的当前状态。

ueeYrev.jpg!web

虽然 ES 是存储系统的状态的一种很好的方法,但是 CQRS 并不一定需要它。对于 CQRS ,领域模型实际上如何存储并不重要,而且这只是一个选项。

读模型和写模型

当我们阅读 CQRS 时,分离模型的概念似乎非常清晰和直接,但在实现过程中似乎并不清楚。写模型的责任是什么?我是否应该将所有数据放入我的读取模型中?嗯,这得看情况!

写模型

我喜欢把我的写作模型看作是系统的核心。这是我的领域模型,它做业务决策,它很重要。它做出业务决策的事实在这里是至关重要的,因为它定义了这个模型的主要职责:它代表系统的真实状态,可以用来做出有价值的决策的状态。这种模式是唯一的真理来源。

如果你想了解更多关于设计领域模型的知识,我推荐你阅读 领域驱动设计 技术哲学。

读模型

在我第一次尝试 CQRS 时,我使用了 WRITE 模型来构建查询……它是 OK 的(或者至少是有效的)。过了一段时间,我们到达了项目中需要花费大量时间进行查询的地方。为什么?因为我们是程序员,优化是我们的第二天性。我们将模型设计为规范化,因此我们的读取端受到连接的影响。我们被迫预先计算一些报告的数据以保持快速。这很有趣,因为实际上我们引入了缓存。在我看来,这是读取模型的最佳定义:它是一个合法的缓存。由于我们必须发布项目,而非功能性的需求没有得到满足,因此,缓存是通过设计来实现的。

标签读取模型可以建议它存储在一个数据库中,仅此而已。实际上读取模型可能非常复杂,你可以使用图形数据库来存储社会连接,使用 RDBMS 来存储财务数据。这是一个多语言持久性很自然的地方。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK