2017-08-01 67 views
1

我并不是经常在StackOverflow上写东西,但我想分享一下我对CQRS/ES实现的理解。我对不同部件的责任感到困惑不解,我想我终于开始讨论这个话题。如果你能告诉我我的理解是否正确,那会很好。我对CQRS/ES的理解是否正确?

一步一步:

  1. 用户创建并触发一个命令,它是代表执行在域中所请求的变化所需的最小值的对象。该命令已分配处理函数。在使用CommandBus的情况下,分离的Dispatcher将触发CommandHandler执行操作。 CommandBus可以使用任何消息传递系统,数据库来实现,或者可能仅仅在内存中工作。

  2. CommandHandler的责任是验证命令,然后根据业务规则创建一个或多个事件并将其发送到EventStore。

  3. 命令可以在Aggregate上调用。 Aggregate是一个由一个或多个实体和ValueObjects组成的对象。聚合的目的是使得不仅可以自己验证命令,而且可以在“聚合”状态下验证命令。

  4. 可以使用消息传递系统或数据库来实现EventStore。事件总是以过去分词动词命名的,例如OrderConfirmed。它们被EventHandlers使用。

  5. EventHandler是一个负责“投影”的功能,这意味着实际在数据库中保存东西。它还可以执行其他操作,例如发送电子邮件,一切都取决于事件的初始目的。

请纠正我,如果我在任何一点都错了。我仍然有几个问题。

  1. 在当CommandHandler正在生成多个事件保存在数据库中的多个对象,我可以将它们分配相同的UUID,后来我也将保存在所有投射对象的情况下?

  2. EventHandler是否可以触发另一个命令或创建另一个事件?这种情况下的最佳做法是什么?

感谢您的回答。

+0

曾听说过分而治之? = P – plalx

+0

@plalx如果你指的是我的第一个。质疑它不是案件。无论如何,据我所知CommandHandler可以生成多个事件。 –

+0

我指的是整个问题。 – plalx

回答

1

一个命令,它是代表在域中

相当右执行所请求的变化所需的最小值的对象。这是一条消息,或者至少是一条消息的表示,而不是一个“对象”。 Applications aren't object oriented at the boundaries,域模型也不是。

EventHandler的责任是验证命令,然后根据业务规则创建一个或多个事件并将其发送到EventStore。

这是一个有点困惑 - 你的意思是不是命令处理程序存在

命令可以叫上聚集。

非常接近 - 这是更正确一点说,命令呼吁聚合根。在最初的Evans公式中,一个聚合将在其边界内指定一个实体作为聚合根,也就是说它将成为所有命令和查询的一个访问点。

在CQRS文献中,命令和查询分开处理;聚合及其根目录仅用于命令。

EventStore可以使用消息传递系统或数据库来实现。

EventStore扮演着记录簿的角色;这意味着它需要支持最低限度的持久性保证 - 只有在“确定”该事件可用于在重新启动时重新加载聚合之前,任何人都不应该被允许查看事件。

事件总是以过去分词动词命名的,例如OrderConfirmed。它们被EventHandlers使用。

事件和命令一样,只是消息;他们可以以任何你喜欢的方式消费。

EventHandler是一个负责“投影”的功能,这意味着实际存储在数据库中的东西。

“投影”,通常被理解为是指创建事件的一些历史(由一个或多个源产生的)的非权威表示。动机是,从历史中创建表示往往需要做大量的工作。如果您尝试支持低延迟查询 - 并且在更改时间和更改显示之间存在一段时间间隔,您可以通过创建更适合快速查询的数据结构来提高延迟时间(或者, ,只需用查询答案的表示预加载缓存)。

备注:非权威很重要。这个数据库不是记录簿,事件存储是。

在CommandHandler生成多个事件来保存数据库中的多个对象的情况下,我可以为它们分配相同的UUID,以后我也将保存在所有投影对象中?

此问题尚不清楚。投影对象将由事件构建而成,因此您通常会希望投影对象中的任何UUID都是您从事件中的数据派生出来的东西。

此外,它通常不是生成事件的命令处理程序 - 这是域模型的责任;这意味着它发生在聚合根后面。

EventHandler是否可以触发另一个命令或创建另一个事件?这种情况下的最佳做法是什么?

一个常见模式是进程管理器,它是一个状态机:将事件传递给它以从一个状态转换到另一个状态,然后查询它以找出哪些命令不应该分派。

3

命令是一条消息,包含描述需要发生的操作的所有必要信息。

关联的命令处理程序在聚合根上调用该命令。聚合根用于验证命令,然后仅分派一个或多个事件。事件处理程序负责将数据投影到读取存储中。 Sagas或状态处理器可用于订阅事件并发布其他消息。

验证需要在聚合根中进行。命令处理程序是另一个可以放置简单数据验证的地方。但是,事件处理程序不是数据验证的地方。如果您在事件处理程序中放置验证,则命令将成功执行,但最终会导致读取存储和任何订阅事件的系统中的数据最终不一致。

事件确实表示由过去有效的命令/聚合根分派的事件。每个事件必须被认为是有效的,因为一旦你重新加载数据,它必须由相应的聚合根重播。业务逻辑可能会随着时间在聚合根上发生变化,但不应该影响事件处理程序。可以使用多个版本的事件和相同类型的相应处理程序来实现。

所有事件,包括关于聚合根(类型,UUID等)的信息都应该存储在事件存储中。事件存储必须一致且持久,因为事件是数据的来源。

我希望这些信息能够让您了解事件处理的工作原理。现在让我试着回答这些问题。

  1. 所有这些事件(通常)应具有相同的聚合根ID和类型(命名空间+类),因此它们可以由相应的聚合根实例重放。他们也应该有一个版本号,确定他们发生的顺序。

  2. 我在第二段中介绍了这一点。