tommy-vercetti
@tommy-vercetti
Symfony/Golang

Domain Events: как лучше делать dispatch?

Все никак не можем решить командой как делать dispatch доменных ивентов. Мой вариант - складировать ивенты в массиве в самой сущности, затем снаружи их диспатчить. Другой человек предлагает через singleton делать dispatch ивента.
Вариант с singleton мне не нравится тем, что домен знает о Application слое, также синглтоны сложно тестировать. Далее без singleton я сам могу контролировать когда диспатчить ивенты. Например, после flush в event listener'е отправить сообщение в RabbitMQ.
Человек, который за singleton, свой вариант аргументирует так:
1. Можно забыть сделать dispatch снаружи
2. При диспатче через singleton сущность изменится сразу (если event listener что-то с ней делает или с другими сущностями), это может пригодиться дальше в коде. Т.е синхронность обработки ивентов, чтобы они сработали в тот же момент. Честно скажу у меня такой необходимости не было.

interface RaiseEventsInterface
{
    /**
     * @return Event[]
     */
    public function popEvents(): array;
}

trait RaiseEventsTrait
{
    protected $events = [];

    public function popEvents(): array
    {
        $events = $this->events;

        $this->events = [];

        return $events;
    }

    protected function raise(Event $event)
    {
        $this->events[] = $event;
    }
}

class User implements RaiseEventsInterface
{
    use RaiseEventsTrait;

    public function __construct(/* args */)
    {
        $this->raise(new UserCreated($this));
    }
}

class CreateUserHandler
{
	public function __invoke(/* args */)
	{
		$user = new User($email, $username /* args */);
  		$dispatcher->dispatch($user->popEvents());
	}
}

Вариант с Singleton
class User
{
    public function __construct(/* args */)
    {
        EventsDispatcherStatic::getInstance()->dispatch(
            new UserCreated($this)
        );
    }
}
  • Вопрос задан
  • 280 просмотров
Решения вопроса 2
mad_maximus
@mad_maximus
Во flush (оберточный) передаете сущность, там делаете `$em->flush()` и после него обходите все события и вызываете диспатч.
Ответ написан
sarapinit
@sarapinit
Точу водой камень
Передавать диспетчера в конструкторе.
Диспетчер делаем синглтоном не через глобальную переменную, регистрируем его в контейнере внедрения зависимостей как синглтон. Это убивает двух зайцев сразу: вы получаете один инстанс с определённой логикой как в синглтоне (не забываем подумать о блокировке, если хотите гарантировать порядок событий) и вы получаете тестирование как с обычным классом.
Событие передавать диспетчеру сразу.
Логику обработки событий в диспетчере (сразу или периодически) инкапсулируем в диспетчере и реализуем в зависимости от задачи.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы