CQRS:在 CommandHandlers 中注入消息总线

CQRS : Injecting a messageBus in CommandHandlers

将整个总线注入 commandHandlers 以便后者可以调用 $this->messageBus->dispatch($events);

听起来是不是一种正确的方法
class OneCommandHandler
{
   private $messageBus;

   //.....

   public function handle(Command $command)
   {
     //..... will at some points hopefully returns DomainEvents
   }

   private function dispatch(DomainEvents $events)
   {
     $this->messageBus->dispatch($events);
   }
}

是不是耦合太大了?

只要你能把事情控制住;喜欢使用依赖注入框架或(未过度滥用的)服务定位器;我认为将总线注入域服务是可以的。

关于拥有存储库...添加另一层只是为了让域服务处于持久性无知状态怎么样?

我的意思是,如果OneCommandHandler是应用域规则并触发域事件,则属于域服务层。域不应该有依赖关系。域应该应用规则和不变量并协调聚合的操作。所有这些都应该是域操作的输入参数。也许您需要应用服务层作为应用服务(安全、日志、持久性等)和领域服务的协调者。

即:

class ProductApplicationService
{
  function ProductNameExchange($productExchangeComand){

      LoggingAppService.LogAction("User ask to exchange product names");
      SecurityAppService.AuthorizeCommand($productExchangeComand);
      $productAggregate1 = ProductRepository.getProductById($productExchangeComand.firsProductId);
      $productAggregate2 = ProductRepository.getProductById($productExchangeComand.secondProductId);

      //rules and invariants
      ProductDomainService.ExchangeNames($productAggregate1, $productAggregate2); 

   }
}

class ProductDomainService{

    function ExchangeNames($productAggregate1, $productAggregate2)(){
    //code to coordinate the 2 aggregates
    $aux = $productAggregate1.name;
    $productAggregate1.setName($productAggregate2.name); //$productAggregate has its own rules to change its name
    $productAggregate2.setName($aux);

  }

}

我什至会说这是推荐的,因为在您的命令处理程序中,您很可能想用处理状态回复发起者,唯一的方法是使用总线 reply/response 功能.

正如在 DDD/CRQS Google 组中讨论的那样,不存在 "one way (or "fire and forget") 命令",因为命令意味着动作、状态变化和这可能会失败,如果没有正确处理失败,系统将处于未定义状态,与幂等查询相比。