public function publish(array $events, CommandBus $commandBus, bool $qeueEvents = false): void
    {
        $eventStreamObjects = [];
        /**
         * @var EventInterface $event
         */
        foreach ($events as $event) {
            // Save the event to the event stream.
            $eventStreamObject = new EventStreamObject();
            $eventStreamObject->setUuid($event->getCommand()->getAggregateUuid());
            $eventStreamObject->setCommandUuid($event->getCommand()->getUuid());
            $eventStreamObject->setPayload($event->getCommand()->getPayload());
            $eventStreamObject->setMessage($event->getMessage());
            $eventStreamObject->setEvent(\get_class($event));
            $eventStreamObject->setAggregateClass($event->getCommand()->getAggregateClass());
            $eventStreamObject->setVersion($event->getCommand()->getOnVersion() + 1);
            $eventStreamObject->setUser($event->getCommand()->getUser());

            // Add to list of eventStreamObjects so we can later notify aggregateSubscribers.
            $eventStreamObjects[] = $eventStreamObject;

            // Add the events to the eventStore.
            if ($qeueEvents) {
                $eventQeueObject = new EventQeueObject($eventStreamObject);
                $this->eventStore->qeue($eventQeueObject);
                $message = 'Qeued event: '.$event->getMessage();
            } else {
                $this->eventStore->add($eventStreamObject);
                $message = 'Persisted event: '.$event->getMessage();
            }

            // Dispatch messages about the events.
            $this->messageBus->dispatch(new Message(
                $message,
                $event::getCode(),
                $event->getCommand()->getUuid(),
                $event->getCommand()->getAggregateUuid(),
                null,
                $event->getCommand()->getPayload()
            ));
        }

        try {
            $this->eventStore->save();

            // Listeners are called even if the events are just qeued!
            // They are not called again when the events are persisted to the event stream!
            $this->invokeListeners($events, $commandBus);

            // Notify subscribers.
            // Subscribers are notified AFTER the events listeners are processed.
            // Subscribers are NOT notified of qeued events.
            if (!$qeueEvents) {
                $this->sendAggregateUpdates($eventStreamObjects);
            }
        } catch (\Exception $e) {
            // Saving to the Event Store failed. This can happen for example when an aggregate version is already taken.
            $this->messageBus->dispatch(new Message(
                $e->getMessage(),
                $e->getCode(),
                null,
                null,
                $e
            ));
        }
    }