関連する作業単位が正常にコミットしたときにのみイベントを発生させるドメインイベントシステムを実装しようとしています。
これを行う主な理由は、イベントを処理するときにこれらの変更が適用されることを期待するデータベースを読み取る他のサブシステムがあるためです。
また、コミットが失敗した場合に、イベントハンドラーが自分の作業(メールの送信など)を実行しないようにします。
システムは、複数のASP.NETアプリケーション(WebForms、MVC、およびWebAPI)です。
_public class Order
{
private Payment _payment;
public int ID { get; private set; }
public decimal Amount { get; private set; }
public void PayUsing(IPaymentProcessor processor)
{
if (_payment != null)
{
throw new InvalidOperationException(
"You can't pay for an order twice");
}
// the Process method may also raise domain events
_payment = processor.Process(Amount);
// Raise saves the event into a static Queue<T>
DomainEvents.Raise(new OrderPaidEvent(this));
}
}
public class OrderFulfillmentService
{
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
private readonly IPaymentProcessor _paymentProcessor;
public OrderFulfillmentService(
IUnitOfWorkFactory unitOfWorkFactory,
IPaymentProcessor paymentProcessor)
{
_unitOfWorkFactory = unitOfWorkFactory;
_paymentProcessor = paymentProcessor;
}
public void Fulfill(int orderId)
{
using (var unitOfWork = _unitOfWorkFactory.Create())
{
var order = unitOfWork.OrdersRepository.GetById(orderId);
order.PayUsing(_paymentProcessor);
unitOfWork.Commit();
// I only want the events to be raised if the Commit succeeds
}
}
}
_
現在の実装では問題が発生します。現在、ドメインレイジングは、すぐに起動してはならないため、それをキューに入れる必要があるためです。キューは静的であり、マルチユーザーシステムに問題を引き起こします。すべてのユーザーが同じキューにアクセスするという明確な問題である、別のユーザーに対して誤ってイベントを発生させたくありません。
これは私が調査したソリューションのリストであり、それらが私のシナリオで機能しない理由です
DomainRaiser
メソッドに渡される非静的PayUsing
クラスを用意します。
DomainRaiser
を渡すことはドメインオブジェクトから行うのが非常に困難です。プロパティを変更してこのオブジェクトを渡すのが面倒な場合、イベントが発生することがあります。_HttpContext.Current.Items
_を使用してユーザー固有のイベントを格納する
私の質問は、作業単位全体が成功した場合にのみイベントを発生させることを考慮しながら、これらのイベントをマルチユーザー環境で確実にキューに入れてディスパッチするにはどうすればよいですか?
まず、各エンティティがDomainRaiser
を参照していることを確認します。これは、エンティティーがリポジトリーで作成または実体化されるときに設定された場合に最適です。各ユーザー/リクエストコンテキストには独自のインスタンスがあり、そのインスタンスがすべてのエンティティに挿入され、コンテキストによって機能します。使用しているRepository/UnitOfWork実装がわからないので、不可能かもしれません。しかし、構築中に呼び出すことができるインターフェースをエンティティに与えることができれば、それは自動化されていると思います。
これは、UnitOfWork
が成功した場合にのみイベントを実行するという2番目の問題も解決します。 DomainRaiser
とUnitOfWork
が1つのコンテキストに単一のインスタンスを持っている場合、これらは相互に対話することができ、これによりこの単純なメソッド呼び出しが行われます。
最後に思い浮かぶのは、いくつかのイベントをprepare
とexecute
の2つの部分に分けることができるということです。イベントの発生元と同じUnitOfWork
でPrepareが呼び出されます。これにより、コミットを待たずに、イベントに関連するデータを読み取ることができます。次に、UnitOfWork
が正常にコミットされ、準備されたデータを使用する場合、実行はフラグ付きで外部で呼び出されます。