web-dev-qa-db-ja.com

アプリケーションサービスでトランザクションを処理するときにドメインイベントを発行するタイミング

これはかなり長い間私を悩ませてきたものです。私はこの1年にわたってさまざまなアプローチを試みましたが、私は常にこれを反映するために戻ってくるので、ここに身を置きます!

ですから、まず共通点を得ます。ブルーブックに従っている場合、トランザクション処理はアプリケーションサービスで行う必要があります。トランザクションを開き、1つまたは複数の集約を取得し、1つの集約のみを適合させて、そのトランザクションをコミットします。 (トランザクションごとに1つの集計)。

次にドメインイベントの理論に従うと、2つのキャンプがあり、1つはコミット前のイベントの発行、もう1つはコミット後のイベントの発行です。私は2番目のもので大丈夫なので、そのコンテキストにとどまりましょう。

わかりました。これについてはさまざまなアプローチがありました。自分で判断したり、他の選択肢を見つけたりできるように説明します。

私は.NETを使用しています。

ですから、私の最初の試みは非常に簡単で、今でも使用しています。

次のような2つのメソッドを持つ汎用リポジトリがあります。

public interface IRepository<TId, T> where T : Aggregate
{
    T GetById(TId id);
    Save(T aggregate);
}

そして、私の保存メソッドでは、いくつかのことを行います:

  • エンティティが既に存在するかどうかを確認し、存在しない場合は追加し、存在する場合は、変更トラッカーに再度アタッチします(現時点ではE.F. Coreを使用します。この部分に焦点を合わせないでください。重要ではありません)
  • トランザクションごとに1つの集計しかないため、SaveChanges()を呼び出すことにしました。リポジトリのSaveメソッドで、親スコープを開いていない場合にデータベースへの変更をコミットするメソッド。だから私は基本的に私のリポジトリでコミットしています。
  • 私の集約基本クラスは、取得してから保存するまでに発生したイベントのリストを保持しています。したがって、このメソッドで変更を保存した後、基本的には自分の集約の非公開イベントを取得し、それらをディスパッチして、リストをクリアします。

ご覧のとおり、Saveメソッドはかなりの数の処理を実行します。おそらく、その一部がその責任の範囲外と見なされます。しかし、私のアプリケーションサービスには実際にはトランザクションが含まれておらず、保存ごとにトランザクションがコミットされるため、この方法を使用することは非常に簡単な経験でした。

私にとっての2番目のアプローチは、エンティティフレームワークの.SaveChangesメソッドをオーバーライドすることでした。基本的に、追跡された集約のリストを取得し、それらのイベントを取得します。次に、実際にコミットするbase.SaveChanges()メソッドを呼び出して、イベントをディスパッチします。

この場合、リポジトリのsaveメソッドは、エンティティを変更トラッカーに保存するときにのみ変更トラッカーにアタッチします(変更追跡なしでエンティティを取得し、.Saveパーツの変更のみを追跡する傾向があります。これは、情報目的で同じ方法ですが、変更されるのはそのうちの1つだけです)。

次に、DbContextを次のようにIUnitOfWorkインターフェイスにラップします。

public interface IUnitOfWork
{
    void Commit();
}

そしてcommitメソッドがcontext.SaveChanges();を呼び出します。メソッド。コミット後にイベントをディスパッチするようにオーバーライドされています。

次に、私のアプリケーションサービスは必要なリポジトリを挿入し、それらの1つで.Save()メソッドを呼び出し、注入されたuowをコミットします。

これら2つについて私が抱えている問題は、E.F。のコンテキストにあります。それらは、私がやりたいことに対して必ずしも100%正しいとは限りません。

アプリケーションサービスでトランザクションスコープを開くと、UoWでリポジトリに保存/コミットするときに、どちらのメソッドも実際にイベントを発行しますが、親トランザクションスコープをロールバックすると、無効な状態になります。

私の最終的な目標は、アプリケーションサービスでトランザクションスコープを常に使用し、そのトランザクションスコープのcommitを使用して、コンテキストの.SaveChanges()メソッドの代わりに実際に変更をコミットし、トランザクションスコープがコミットされた後にのみイベントをディスパッチすることです。

さて、私の質問は次のとおりです。最初の2つのアプローチはあなたにとって正しいように見えますか?そして、私は細かい詳細について心配していますか?私の3番目のアイデアは実際に正しい方法で達成できますか?余分な代替案を見逃す可能性はありますか?

ありがとうございます !

2
TanguyB

送信トレイパターン の使用を検討しましたか?ドメインイベントをデータベースに保存し、データベースからディスパッチします(別のワーカーを介して、または標準のデータベースメッセージングプラットフォームコネクタによって)。

このアプローチでは、ロールバックの場合にイベントが送信トレイに永続化されないため、データベースとイベントは一貫します。 2番目の利点は、メッセージングプラットフォームが一時的に利用できない場合にイベントの発行を再試行できるため、メッセージが失われないことです。

詳細については、次の記事を確認してください: 12

1
Peter Csala