4

我目前正在研究是否可以在构建特定系统时应用 CQRS,并且有一些我无法轻易找到答案的问题。

命令可用性/验证

用户可以对特定实体做什么通常不仅与用户的角色相关,还与该特定用户与该实体的关系(例如作者)和该实体的状态(公共、存档等)相关。

在我看来,在 CQRS 中,用户操作映射到负责处理它们的命令,但不清楚如何确定哪些操作以及命令可用。

返回每个读取模型的可用命令列表似乎是不合适的,因为由于需要一致性,我们必须检查仅与用户角色相关的命令(例如菜单项)。

当然,命令必须在事务中再次进行验证,以防另一个用户更改状态使命令无效)。

抵抗需求变化

以我的经验,维护一个逻辑冗长且与特定业务对象相关的类/表过多的系统是一场噩梦。

在 CQRS 中,一个特定的业务实体可能有多个读取模型。当需要更改该实体时,也应修改所有相关的读取模型。

为了可维护性,有必要以某种方式将它们关联起来,以便在重构时很容易看出。

在相关说明中,有很多过于具体的命令也会导致可维护性问题 - 我假设每个用例一个命令应该最有效是正确的吗?

读取模型和不修改域模型的命令

在 CQRS 中,命令应该更新读取模型,同时用户可以访问它的旧版本。

在我看来,有两种特殊情况会导致问题。

首先,有些命令不会修改域模型本身(可能只是状态),而是执行一些涉及第三方系统/框架/电子邮件等的操作,在某些情况下,这些命令可能需要相当长的时间才能运行。

正如我在这里看到的,我们需要一个将域模型与命令执行状态相结合的读取模型。此读取模型可用作当前正在处理的项目的历史记录或列表。

其次,一些命令会产生结果。当命令成功完成时,需要向用户显示此结果,并且在某些情况下必须在一段时间后丢弃,甚至是文件。因此,必须有一种方法将这些命令的结果存储在数据库中,并将它们与特定的命令实例相关联。换句话说,有一个临时读取模型。

读取模型表与内存缓存

另外我的想法是正确的,使用二级 ORM 缓存(用于查询结果),不需要为读取模型提供数据库表,而是 ORM 可以在第一次执行查询时生成它们,缓存结果并使其无效当模型实体改变时它们会自动出现。这种方法似乎是一个很好的起点,可以强制执行 CQRS 接口/模式,但可以更改并且作为奖励可以支持动态投影(当用户选择要查看的列等时)。

4

1 回答 1

4

总的来说,我不确定你具体提出的问题,所以我会尽力回答这些问题。我希望这有帮助。如果我错过了您的问题的重点,我会澄清一下-请告诉我。

命令可用性/验证

我不确定您对这部分的问题是什么,但本质上是您的用户行为决定了您可以使用哪些命令。该命令对于该特定用户是否有效可以由提交命令的 UI 控制器(或任何机制)处理,也可以由接收命令的域处理。该命令应包含足够的信息,以便域对其进行评估,然后更改其状态并引发事件。

抵抗需求变化

如果实体发生变化,并非所有相关的读取模型都需要更改——这实际上取决于读取模型的目的。维护读取模型的一些方法可能是为每个相关的读取模型集使用不同的模式,或者使用命名标准(例如 ORDER_xxx)。我更喜欢前者,因为它更干净(至少从我的角度来看)。

读取模型和不修改域模型的命令

命令并不总是应该更新读取模型。如果域中的业务规则引发了处理和持久化的适当事件,他们可以更新读取模型。仅仅因为发出命令并不意味着读取模型将被更新。

如果您遵循 CQS(和 CQRS),那么您的命令不应返回结果。命令返回无效。命令是你告诉系统做某事。您所描述的是一个两步过程:发出命令,然后发出读取请求。读取请求很可能会产生“旧”数据(即在命令更新读取模型之前)。有几种方法可以处理这个问题。您可以进行投票 - 这不是一种非常优雅的方式,但它确实有效。您可以在发出命令后将用户带到中间页面(“谢谢您的订单,您想订购其他东西吗?”),然后当用户返回时,他们会看到他们更新的列表(因为命令有现在处理)。再次,不理想。但这就是最终一致性的本质。没有真正的答案——它

对于只是更新状态或与第三方系统交互的命令,这些命令不能在不让用户等待它们完成的情况下离线完成吗?我认为这是让您的系统反映您的用户将如何使用系统的另一种情况。

读取模型表与内存缓存

如果你问这是否是一个好主意,我不太确定。您对这个内存缓存的出发点是什么?当系统重新启动时会发生什么——你的模型会发生什么?你重建它,你从哪里以及如何恢复状态?您是否为此使用事件溯源,并且您将重播上次快照中的所有事件以使您的域模型到位?

在不知道这些问题的答案的情况下,我无法给出答案。

于 2012-08-30T20:51:21.577 回答