为什么命令和事件要分开表示?

发布于 2024-10-17 01:07:50 字数 114 浏览 8 评论 0原文

在强调事件的架构中,命令和事件有什么区别?我能看到的唯一区别是命令通常由系统外部的参与者获取/调用,而事件似乎由系统中的处理程序和其他代码获取。然而,在我见过的许多示例应用程序中,它们具有不同(但功能相似)的界面。

What is the difference between commands and events in architectures that emphasize events? The only distinction I can see is that commands are usually sourced/invoked by actors outside the system, whereas events seem to be sourced by handlers and other code in a system. However, in many example applications I have seen, they have different (but functionally similar) interfaces.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

总攻大人 2024-10-24 01:07:50

命令可以被拒绝。

事件已经发生了。

这可能是最重要的原因。在事件驱动的架构中,毫无疑问,引发的事件代表已经发生的事情

现在,因为命令是我们希望发生的事情,而事件是已经发生的事情,所以在命名这些事情时我们应该使用不同的动词。这驱动了单独的表示。

我可以看到命令通常是
由外部参与者来源/调用
系统,而事件似乎是
由处理程序和其他代码来源
一个系统

这是它们被单独表示的另一个原因。概念清晰。

命令和事件都是消息。但它们实际上是独立的概念,并且应该明确地对概念进行建模。

Commands can be rejected.

Events have happened.

This is probably the most important reason. In an event-driven architecture, there can be no question that an event raised represents something that has happened.

Now, because Commands are something we want to happen, and Events are something that has happened, we should be using different verbs when we name these things. This drives separate representations.

I can see is that commands are usually
sourced/invoked by actors outside the
system, whereas events seem to be
sourced by handlers and other code in
a system

This is another reason they are represented separately. Conceptual clarity.

Commands and Events are both Messages. But they are in fact separate concepts, and concepts should be modeled explicitly.

等待我真够勒 2024-10-24 01:07:50

事件是过去的事实。

命令只是一个请求,因此可能会被拒绝。

命令事件
目的调用行为发生的事情
所有权命令 归消费者所有 事件归发布者
消费者一个消费者零个或多个消费者
发送者许多发送者 单个发布者
命名动词过去时态

命令的一个重要特征是它应该是
仅由单个接收器处理一次。这是因为命令是
您想要在应用程序中执行的单个操作或事务。
例如,不应处理相同的订单创建命令
不止一次。这是命令和命令之间的重要区别
事件。事件可能会被多次处理,因为许多系统或
微服务可能对此事件感兴趣。 'msdn'

The event is a fact from the past.

The command is only a request, and thus may be refused.

CommandsEvents
PurposeInvoke BehaviorSomething Happened
OwnershipCommand Owned by ConsumerEvent Owned by Publisher
ConsumersOne ConsumerZero or Many Consumers
SendersMany SendersSingle Publisher
NamingVerbPast Tense

An important characteristic of a command is that it should be
processed just once by a single receiver. This is because a command is
a single action or transaction you want to perform in the application.
For example, the same order creation command should not be processed
more than once. This is an important difference between commands and
events. Events may be processed multiple times because many systems or
microservices might be interested in the event. 'msdn'

嗳卜坏 2024-10-24 01:07:50

此外,除了此处公开的所有答案之外,事件处理程序还可以在收到事件发生的通知后触发命令。

例如,在创建客户后,您还想初始化一些帐户值等。在客户 AR 将事件添加到 EventDispatcher 并由 CustomerCreatedEventHandler 对象接收后,此处理程序可以触发命令的调度,该命令将执行您需要的任何内容等。

此外,还有 DomainEvents 和 ApplicationEvents。区别只是概念上的。您希望首先分派所有域事件(其中一些可能会产生应用程序事件)。我这是什么意思?

发生 CustomerCreatedEvent 后初始化帐户是 DOMAIN 事件。向客户发送电子邮件通知是一个应用程序事件。

不应该混合它们的原因很清楚。如果您的 SMTP 服务器暂时关闭,并不意味着您的域操作会受到影响。您仍然希望保持聚合的未损坏状态。

我通常将事件添加到聚合根级别的调度程序中。该事件是 DomainEvents 或 ApplicationEvents。可以是两者,也可以是其中很多。一旦我的命令处理程序完成并且我回到堆栈中执行命令处理程序的代码,然后我检查我的调度程序并调度任何其他域事件。如果所有这些都成功,那么我将关闭交易。

如果我有任何应用程序事件,现在就是调度它们的时候了。发送电子邮件不一定需要打开与数据库的连接,也不一定需要打开事务范围。

我有点偏离了最初的问题,但对您来说理解如何在概念上以不同的方式对待事件也很重要。

然后你有 Sagas.... 但这超出了这个问题的范围:)

这有意义吗?

Also, in addition to all the answers here exposed, an event handler may be able to trigger a command as well after receiving notification that an event occurred.

Say for example that after you create a Customer, you also want to initialize some accounts values, etc. After your Customer AR add the event to the EventDispatcher and this is received by a CustomerCreatedEventHandler object, this handler can trigger a dispatch of a command which will execute whatever you need, etc.

Also, there are DomainEvents and ApplicationEvents. The difference is simply conceptual. You want to dispatch all your domain events first (some of them may produce Application Events). What do I mean by this?

Initializing an account after a CustomerCreatedEvent has occurred is a DOMAIN event. Sending an email notification to the Customer is an Application Event.

The reason you shouldn't mix them is clear. If your SMTP server is temporarily down, that doesn't mean that your DOMAIN OPERATION should be affected by that. You still want to keep a non-corrupted state of your aggregates.

I usually add events to my Dispatcher at the Aggregate Root level. This events are either DomainEvents or ApplicationEvents. Can be both and can be many of them. Once my command handler is done and I am back in the stack to the code that execute the Command handler, then I check my Dispatcher and dispatch any other DomainEvent. If all of this is successful, then I close the transaction.

If I have any Application Events, this is the time to dispatch them. Sending an email doesn't necessarily need an open connection to a database nor a transaction scope open.

I strayed away a little bit from the original question but it is also important for you to understand how events may also be conceptually treated differently.

Then you have Sagas.... but that's WAYYYY OFF of the scope of this question :)

Does it make sense?

仙气飘飘 2024-10-24 01:07:50

在完成一些示例,特别是 Greg Young 演示文稿 (http://www.youtube.com/watch ?v=JHGkaShoyNs) 我得出的结论是命令是多余的。它们只是来自您的用户的事件,他们确实按下了该按钮。您应该以与其他事件完全相同的方式存储它们,因为它是数据,并且您不知道是否要在将来的视图中使用它。您的用户确实添加了该商品,然后又从购物篮中删除了该商品,或者至少尝试这样做。您稍后可能希望使用此信息来提醒用户这一点。

After working through some examples and especially the Greg Young presentation (http://www.youtube.com/watch?v=JHGkaShoyNs) I've come to the conclusion that commands are redundant. They are simply events from your user, they did press that button. You should store these in exactly the same way as other events because it is data and you don't know if you will want to use it in a future view. Your user did add and then later remove that item from the basket or at least attempt to. You may later want to use this information to remind the user of this at later date.

云归处 2024-10-24 01:07:50

只是为了补充这些精彩的答案。我想指出耦合方面的差异。

命令针对特定处理器。因此,命令发起者和处理器之间存在一定程度的依赖/耦合。

例如,UserService 在创建新用户时会向 EmailService 发送“发送电子邮件”命令。

事实上,UserService 知道它需要 EmailService,这已经是耦合了。如果 EmailService 更改其 API 架构或出现故障,则会直接影响 UserService 功能。


事件不针对任何特定的事件处理程序。因此,事件发布者变得松散耦合。它不关心什么服务消耗其事件。事件的 0 个消费者甚至是有效的。

例如,UserService 在创建新用户时会发布“用户创建事件”。 EmailService 可能可以使用该事件并向用户发送电子邮件。

这里,UserService 不知道EmailService。他们是完全脱钩的。如果 EmailService 出现故障,或者更改业务规则,我们只需编辑 EmailService


这两种方法都有优点。纯粹的事件驱动架构设计很难跟踪,因为它的耦合过于松散,尤其是在大型系统上。而命令重架构具有高耦合度。因此,良好的平衡是理想的。

希望这是有道理的。

Just to add to these great answers. I'd like to point out differences with regards to coupling.

Commands are directed towards a specific processor. Thus there is some level of dependence/coupling with the Command initiator and the processor.

For example, a UserService upon creating a new user sends a "Send Email" Command to the EmailService.

The fact that the UserService knows that it needs the EmailService, that is already coupling. If EmailService changes its API schema or goes down, it directly affects the UserService function.


Events are not directed towards any specific event handler. Thus the event publisher becomes loosely coupled. It does not care what service consumes its event. It's even valid to have 0 consumer of an Event.

For example, a UserService upon creating a new user publishes a "User Created Event". Potentially an EmailService can consume that event and sends an email to the user.

Here the UserService is not aware of the EmailService. They are totally decoupled. If the EmailService goes down, or changes business rules, we only need to edit the EmailService


Both approaches have merits. A purely Event Driven Architectural design is harder to track since it is too loosely coupled, especially on large systems. And a Command heavy Architecture have high level of coupling. So a good balance is ideal.

Hope that makes sense.

揪着可爱 2024-10-24 01:07:50

它们分别代表,因为它们代表非常不同的事物。
正如 @qstarin 所说,命令是可以拒绝的消息,成功后将产生一个事件。
命令和事件是Dto,它们是消息,它们在创建实体时看起来非常相似,但从那时起,就不一定了。

如果您担心重用,那么您可以使用命令和事件作为(消息)有效负载的信封

class CreateSomethingCommand
{
    public int CommandId {get; set;}

    public SomethingEnvelope {get; set;}
 }

,但是,我想知道的是您为什么问:D,即您有太多命令/事件吗?

They are represented separetly because they represent very different things.
As @qstarin said commands are messages that can be rejected, and that on success will produce an event.
Commands and events are Dtos, they are messages, and they tend to look very similar when creating and entity, however from then on, not necessarily.

If you are worried about reuse, then you could use commands and events as envelopes to your (messge) payload

class CreateSomethingCommand
{
    public int CommandId {get; set;}

    public SomethingEnvelope {get; set;}
 }

however, what I d like to know is why are you asking :D ie do you have too many commands/events?

心房的律动 2024-10-24 01:07:50

除了上面提到的概念差异之外,我认为还存在与常见实现相关的另一个差异:

事件通常在需要轮询事件队列的后台循环中进行处理。任何有兴趣对事件采取行动的一方通常都可以注册一个回调,该回调作为事件队列处理的结果而被调用。因此一个事件可能是一对多

命令可能不需要以这种方式处理。命令的发起者通常可以访问命令的预期执行者。例如,这可以采用发送给执行器的消息队列的形式。因此,命令旨在用于单个实体

In addition to the conceptual differences mentioned above, I think there is another difference related to common implementations:

Events are typically processed in a background loop that needs to poll the event queues. Any party interested in acting on the event may, usually, register a callback that is called as a result of the event queue processing. So an event may be one to many.

Commands may not need to be processed in such a manner. The originator of the command will typically have access to the intended executor of the command. This could be, for example, in the form of a message queue to the executor. Thus a command is intended for a single entity.

高跟鞋的旋律 2024-10-24 01:07:50

您无法根据命令重新计算状态,因为通常每次处理它们时都会产生不同的结果。

例如,想象一个 GenerateRandomNumber 命令。每次调用它时,都会产生不同的随机数 X。因此,如果您的状态取决于该数字,则每次从命令历史记录重新计算状态时,您都会得到不同的状态。

事件解决了这个问题。当您执行命令时,它会产生一系列代表命令执行结果的事件。例如,GenerateRandomNumber 命令可以生成一个 GenerateNumber(X) 事件来记录生成的随机数。现在,如果您从事件日志中重新计算状态,您将始终获得相同的状态,因为您将始终使用由特定命令执行生成的相同数字。

换句话说,命令是具有副作用的函数,事件记录命令的特定执行的结果。

注意:
您仍然可以记录命令历史记录以用于审核或调试目的。要点是要重新计算状态,您使用事件历史记录,而不是命令历史记录。

You cannot recompute a state based on commands, because in general they can produce different outcomes each time they are processed.

For example, imagine a GenerateRandomNumber command. Each time it's invoked it will produce a different random number X. Thus, if your state depends on this number, each time you recompute your state from the commands history, you'll get a different state.

Events solve this problem. When you execute a command, it produces a sequence of events that represent the outcome of the command execution. For example, the GenerateRandomNumber command could produce a GeneratedNumber(X) event that logs the generated random number. Now, if you recompute your state from the events log, you'll always get the same state, because you'll always use the same number that was generated by a particular execution of the command.

In other words, commands are functions with side-effects, events record the outcome of a particular execution of a command.

Note:
You can still record a history of commands for audit or debugging purposes. The point is that to recompute the state, you use the history of events, not the history of commands.

红墙和绿瓦 2024-10-24 01:07:50

我认为昆汀-桑汀的答案需要补充的是:

将请求封装为对象,从而让您参数化
具有不同请求、队列或日志请求的客户端以及支持
可撤消的操作。

来源

I think something to add to quentin-santin's answer is that they:

Encapsulate a request as an object, thereby letting you parameterize
clients with different requests, queue or log requests, and support
undoable operations.

Source.

别把无礼当个性 2024-10-24 01:07:50

让我们将功能核心、命令式 shell 模式融入其中。事件与功能核心相关,命令与命令式 shell 相关。也就是说,事件存在于纯粹之中,命令存在于不纯粹之中。与任何 CLI 一样,shell 会处理杂质。

这可以从命令被验证并且有时被拒绝中看出。还可以看出,命令可以依赖事件世界中不允许的操作。例如,双陆棋游戏中的掷骰子涉及发出 roll 命令,其生成结果将在 rolled 事件中记录。这种分离的价值可以进一步体现在获取或重播事件的能力上。

虽然实时事件可能会冒泡到 shell 并导致发出新命令,但重播事件却不能。它们不得引起进一步的副作用。

Let's throw the functional core, imperative shell pattern into the mix. Events correlate to the functional core and commands to the imperative shell. That is, events reside in the pure and commands in the impure. The shell, like any CLI, handles the impurities.

This can be seen in that commands are validated and sometimes rejected. It can also be seen in that commands can rely on operations not permitted in the world of events. For example, rolling dice in a game of Backgammon involves issuing a roll command and its result when generated would be memorialized in a rolled event. The value of this separation can be further seen in the ability to source or replay events.

While live events may bubble up to the shell and cause new commands to be issued, replayed events cannot. They must cause no further side effects.

满身野味 2024-10-24 01:07:50

扩展@LeviFuller评论中的概念,我还使用命令和事件模式分别表示“恰好一个”和“0个或多个”侦听器,但与@froi答案不同,两者仍然是解耦的。

在这种架构中,命令发布者仍然不知道谁将处理命令(就像事件一样)。不同之处在于,发布者确信有人将处理该命令。因此,虽然您可以将任何命令建模为事件,但这样做是不可取的,因为发布者没有这种保证。

当考虑命令处理器之间的转换时,这一点变得尤其重要。转变可能在很多场景下发生——例如,如果命令处理器是一个网络服务,它可能需要停机维护;或者,在基于 UI 的应用程序中,命令处理器可以代表用户所在的屏幕,并且用户正在移动到不同的屏幕。

在转换期间,您通常希望确保传入的命令仅由新处理器处理。旧处理器将尽快取消注册,这意味着在没有处理器注册该命令类型时可能会存在间隙 - 并且命令中心(类似于事件中心)应维护在此期间接收到的命令队列。这还可能包括当时部分处理的命令,因为旧处理器应在注销自身后重新提交“仍要做的工作”。

使用事件这一切都是不可能的。如果没有人在监听某个事件,则意味着没有人关心它,这没关系。在这种情况下,事件不需要排队。如果事件订阅者在开始关闭时尚未完成事件处理,则通常无需重新提交“仍需完成的工作”,因为它不再相关。事件是“一劳永逸”的,因为在提交事件后不会发生什么,但是使用(有效的)命令,即使在转换期间,也会期望执行某些操作。

Expanding on the concepts from @LeviFuller comment, I also used command and event patterns to represent "exactly one" and "0 or more" listeners respectively, but in a different way from @froi answer, where both are still decoupled.

In this architecture, a command publisher still does not know who is going to process the command (much like with events). The difference is that the publisher is assured that someone will process the command. So while you could model any command as an event, it would be undesirable to do so, because the publisher doesn't have that assurance.

This becomes especially important when considering the transition between command processors. A transition may occur in many scenarios - for example, if the command processor is a network service, it may need to go down for maintenance; alternatively, in a UI based application, the command processor could represent the screen the user is on, and the user is moving to a different screen.

During a transition, you typically want to ensure that commands coming in are only processed by the new processor. The old processor would deregister itself as soon as possible, meaning that there may be a gap while no processor is registered for the command type - and the command hub (analogous to an event hub) should maintain a queue of commands received during this time. This may also include partially processed commands from that time, as the old processor should resubmit the "work still to do" after deregistering itself.

None of this would be possible using events. If nobody is listening to an event, it means nobody cares about it, and that's okay. Events would not need to be queued in that case. If an event subscriber had not finished processing an event at the time it begins to shut down, there's typically no need to resubmit the "work still to do" as it's no longer relevant. Events are "fire and forget" in that there is no expectation of what happens after they are submitted, but with a (valid) command there is an expectation that something will be done, even during a transition.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文