Это звучит как правильный подход к залить весь автобус в 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);
}
}
Не слишком ли много связи?
Я бы даже сказал, что это рекомендуется, поскольку в вашем обработчике команд вы, скорее всего, захотите ответить отправителю со статусом обработки, и единственный способ сделать это — задействовать функцию ответа / ответа шины.
Как обсуждалось в группе Google DDD / CRQS, не существует такой вещи, как «команды в одну сторону (или« запустить и забыть »)», поскольку команда означает действие, изменение состояния, и это может привести к сбою, оставляя систему в неопределенном состоянии. если ошибка не обрабатывается должным образом, по сравнению с запросом, который идемпотентен.
Пока вы можете держать вещи под контролем; например, использование инфраструктуры внедрения зависимостей или (не чрезмерно используемого) сервисного локатора; Я думаю, что все в порядке, вводя шину в доменные службы.
О наличии хранилища … Как насчет добавления еще одного слоя, чтобы сохранить доменные службы в постоянном невежестве?
Я имею в виду, что если 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);
}
}