CQRSパターンが存在するか、メディエーターパターンなしで機能するか?
つまり、常にコマンドがあり、これらのコマンドは、なんらかのロジックを実行するハンドラーに何らかの方法でディスパッチされる必要があります。
CQRSパターンが機能するためにメディエーターパターンに依存するということですか?
CQRSで見たすべての例はメディエーターパターンを使用していますが、それはこのパターンの不可欠な部分であることを意味しますか?
または、メディエーターパターンをまったく使用しなくても機能しますか?
しかし、それはそれではあまり意味がありません...
Command Query Responsibility Separation(CQRS)は、読み取りと書き込みを2つの異なるモデルに分離します。
「従来の」アーキテクチャは次のようになります。
CQRSアーキテクチャは次のようになります。
CQRSダイアグラムに、UIにサービスを提供するエンドポイントが1つだけでなく、2つあることに注意してください。
Mediatorパターンは、一連のオブジェクトの相互作用方法をカプセル化するオブジェクトを定義します。 Mediatorは、オブジェクトが相互に明示的に参照しないようにすることで疎結合を促進し、相互作用を個別に変化させることができます。
メディエーターパターンは、コンポーネントがお互いを明示的に呼び出さないようにしますが、代わりにメディエーターへの呼び出しを通じて呼び出します。次の例では、メディエーターがすべてのコンポーネントを登録してから、それらのSetState
メソッドを呼び出します。
public interface IComponent
{
void SetState(object state);
}
public class Component1 : IComponent
{
public void SetState(object state)
{
throw new NotImplementedException();
}
}
public class Component2 : IComponent
{
public void SetState(object state)
{
throw new NotImplementedException();
}
}
// Mediates the common tasks
public class Mediator
{
public IComponent Component1 { get; set; }
public IComponent Component2 { get; set; }
public void ChangeState(object state)
{
this.Component1.SetState(state);
this.Component2.SetState(state);
}
}
メディエーターは自然にObserver PatternとEvent Aggregator Patternに適しています。イベントはコンポーネントをメディエーターに登録する自然な方法です:
このオブザーバーパターンの図が、上記のメディエーターパターン図とどの程度類似しているかに注意してください。
では、メディエーターパターンはCQRSとどのように関係しているのですか?
それほどではありません。
まあ、CQRSでメディエーターを使用できることと、メディエーターでオブザーバーパターンを使用できること、そしておそらく両方で他の半ダースの有用なソフトウェアパターンを使用できることは別です。
さらに読む
Martin Fowlerによるコマンドクエリの責任分離
MSDNのC#でのCQRSの例
ウィキペディアでのコマンドクエリの責任の分離
Sourcemaking.comのオブザーバーパターン
Event Aggregator(Fowler)
はい。メディエーターは必要ありません。
次のインターフェースを想定します。
public interface ICommandHandler<in TCommand> where TCommand : ICommand
{
void Execute(TCommand command);
}
これで、次のコマンドができました。
public class CreateCustomer : ICommand { /* ... */ }
そしてコントローラー(ASP.NET MVCの例):
public CustomerController
{
CustomerController(ICommandHandler<CreateCustomer> handler) { Handler = handler; }
public ActionResult CreateCustomer(CreateCustomer model)
{
if (!ModelState.IsValid) { return View(); }
Handler.Execute(model);
return View();
}
}
依存関係の注入は、コマンドハンドラの作成を処理します。コマンドの実行に追加の動作(ロギングなど)を適用する場合は、依存関係注入内にインターセプターを実装できます。オブジェクトは、元のコマンドハンドラを含むデコレータまたはコマンドをWebサービスに送信するプロキシである可能性があります。すべてのOOPものは注入によって可能です。
別の方法としては、ファクトリーパターンが考えられます。
public ActionResult CreateCustomer(CreateCustomer model)
{
var handler = HandlerFactory.CreateHandlerFor<CreateCustomer>();
handler.Execute(model);
return View();
}
(あなたが工場が好きなら)うまくいきます。
メディエーターは、2つのオブジェクトを分離するための単なる方法です。利点は、必要な複数のコマンドハンドラーの代わりにメディエーターを挿入するだけで(依存関係の注入と比較して)、作成されたコマンドハンドラーのローカルコピーを保持する必要がない(ファクトリパターンと比較して)ことです。
ただし、メディエーターには欠点もあります。たとえば、CustomerControllerがコマンドCreateProductを呼び出すことを制御できません。特定のコマンドハンドラーのみを挿入することで、コントローラーが作成できるコマンドを制限できます(必要な場合)。