web-dev-qa-db-ja.com

コマンドとイベントが別々に表されるのはなぜですか?

イベントを強調するアーキテクチャのコマンドとイベントの違いは何ですか?私が見ることができる唯一の違いは、コマンドは通常、システム外のアクターによってソース/呼び出されるのに対し、イベントはシステム内のハンドラーや他のコードによってソースされているように見えることです。ただし、私が見た多くのサンプルアプリケーションでは、それらのインターフェースは異なります(ただし機能的には類似しています)。

67
alphadogg

コマンドは拒否できます。

イベントが発生しました。

これがおそらく最も重要な理由です。イベント駆動型アーキテクチャでは、発生したイベントが発生したを表すことは間違いありません。

ここで、コマンドはwantが発生するものであり、イベントはhasが発生するものであるため、これらに名前を付けるときは異なる動詞を使用する必要があります。これは、別々の表現を駆動します。

コマンドは通常、システムの外部のアクターによってソース/呼び出されるのに対し、イベントはシステム内のハンドラーやその他のコードによってソースされているように見えることがわかります

これは、それらが別々に表されるもう1つの理由です。概念的な明快さ。

コマンドとイベントはどちらもメッセージです。しかし、それらは実際には別々の概念であり、概念は明示的にモデル化する必要があります。

133
quentin-starin

また、ここで公開されているすべての回答に加えて、イベントハンドラーは、イベントが発生したという通知を受け取った後にコマンドをトリガーすることもできます。

たとえば、Customerを作成した後、いくつかのアカウント値なども初期化したいとします。CustomerARがEventDispatcherにイベントを追加し、これがCustomerCreatedEventHandlerオブジェクトによって受信された後、このハンドラーはコマンドのディスパッチをトリガーできます。必要なものをすべて実行します。

また、DomainEventsとApplicationEventsもあります。違いは単に概念的なものです。すべてのドメインイベントを最初にディスパッチしたい場合(それらの一部はアプリケーションイベントを生成する場合があります)。これはどういう意味ですか?

CustomerCreatedEventの発生後にアカウントを初期化するのはDOMAINイベントです。お客様への電子メール通知の送信は、アプリケーションイベントです。

それらを混ぜてはいけない理由は明らかです。 SMTPサーバーが一時的にダウンしている場合でも、ドメイン操作が影響を受けるわけではありません。アグリゲートの破損していない状態を維持する必要があります。

通常、Aggregate RootレベルでDispatcherにイベントを追加します。このイベントは、DomainEventsまたはApplicationEventsのいずれかです。両方にすることができ、それらの多くにすることができます。コマンドハンドラーが完了し、スタックに戻ってコマンドハンドラーを実行するコードに戻ったら、Dispatcherをチェックして、他のDomainEventをディスパッチします。これらすべてが成功した場合は、トランザクションを閉じます。

アプリケーションイベントがある場合は、ここでそれらをディスパッチします。電子メールを送信するために、データベースへの接続を開いたり、トランザクションスコープを開いたりする必要はありません。

元の質問から少し離れましたが、イベントの概念的な扱い方を理解することも重要です。

それからあなたはSagasを持っています...しかし、それはこの質問の範囲からはずれたものです:)

それは意味がありますか?

5

いくつかの例、特にGreg Youngのプレゼンテーションを検討した後( http://www.youtube.com/watch?v=JHGkaShoyNs )コマンドは冗長であるという結論に達しました。これらは単にユーザーからのイベントであり、そのボタンを押しました。これらはデータであり、将来のビューで使用するかどうかわからないため、他のイベントとまったく同じ方法で保存する必要があります。ユーザーがそのアイテムを追加してバスケットから削除したか、少なくとも削除しようとしました。後でこの情報を使用して、後日このことをユーザーに通知することができます。

4
Ant

上記の概念の違いに加えて、一般的な実装に関連する別の違いがあると思います。

イベントは通常、バックグラウンドループで処理されますpollイベントキュー。通常、イベントの処理に関心のある任意のパーティが、イベントキュー処理の結果として呼び出されるコールバックを登録できます。つまり、イベント1対多の可能性がありますです。

コマンドは、このような方法で処理する必要がない場合があります。コマンドの発信者は通常、コマンドの意図されたエグゼキューターにアクセスできます。これは、たとえば、エグゼキュータへのメッセージキューの形式にすることができます。したがって、コマンドは単一のエンティティを対象としていますです。

2
dww

それらは非常に異なるものを表すため、別々に表されます。 @qstarinが言ったように、コマンドは拒否できるメッセージであり、成功するとイベントが生成されます。コマンドとイベントはDtosであり、それらはメッセージであり、エンティティを作成するときに非常によく似ている傾向がありますが、それ以降は必ずしもそうではありません。

再利用が心配な場合は、コマンドとイベントを(メッセージ)ペイロードのエンベロープとして使用できます。

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

    public SomethingEnvelope {get; set;}
 }

しかし、私が知りたいのは、なぜ:Dを求めているのですか?つまり、コマンド/イベントが多すぎますか?

2
roundcrisis

私はクエンティン・サンティンの答えに追加するものはそれらが次のことだと思います:

リクエストをオブジェクトとしてカプセル化します。これにより、さまざまなリクエストでクライアントをパラメータ化し、リクエストをキューまたはログに記録して、取り消し可能な操作をサポートできます。

ソース

1

eventは過去の事実です。

commandは要求にすぎないため、拒否される場合があります。

コマンドの重要な特徴は、単一のレシーバーで一度だけ処理する必要があることです。これは、コマンドがアプリケーションで実行する単一のアクションまたはトランザクションであるためです。たとえば、同じ注文作成コマンドを複数回処理することはできません。これは、コマンドとイベントの重要な違いです。多くのシステムまたはマイクロサービスがイベントに関心を持っている可能性があるため、イベントは複数回処理される可能性があります。 「msdn」

1
Ali Bayat

コマンドに基づいて状態を再計算することはできません。一般に、コマンドは処理されるたびに異なる結果を生成する可能性があるためです。

たとえば、GenerateRandomNumberコマンドを想像してみてください。呼び出されるたびに、異なる乱数Xが生成されます。したがって、状態がこの数値に依存している場合、コマンド履歴から状態を再計算するたびに、異なる状態が得られます。

イベントはこの問題を解決します。コマンドを実行すると、コマンド実行の結果を表す一連のイベントが生成されます。たとえば、GenerateRandomNumberコマンドは、生成された乱数を記録するGeneratedNumber(X)イベントを生成できます。ここで、イベントログから状態を再計算すると、常に同じ状態になります。これは、コマンドの特定の実行によって生成されたのと同じ番号を常に使用するためです。

つまり、コマンドは副作用のある関数であり、イベントはコマンドの特定の実行の結果を記録します。

注:監査またはデバッグの目的でコマンドの履歴を記録できます。ポイントは、状態を再計算するために、コマンドの履歴ではなくイベントの履歴を使用することです。

0