A command object is a controller action you can queue later

“Send the confirmation in the background” arrives as a requirement long after the code that sends it has settled into a controller method, wired to a request and a session. If the operation is already an object carrying its own arguments, moving it off the request is a change to who calls the handler and to nothing else.

class ConfirmOrder            // the arguments, and nothing else
{
    public $orderId;
    public $notifyBy;

    public function __construct($orderId, $notifyBy)
    {
        $this->orderId  = $orderId;
        $this->notifyBy = $notifyBy;
    }
}

class ConfirmOrderHandler     // the behaviour, and its dependencies
{
    public function handle(ConfirmOrder $command)
    {
        $order = $this->orders->find($command->orderId);

        $this->mailer->send(new Confirmation($order), $command->notifyBy);
        $this->stock->commit($order);
    }
}

// in the controller, today
$this->bus->execute(new ConfirmOrder($id, 'email'));

// and later, with no change to the handler at all
$this->queue->push('ConfirmOrderHandler', serialize(new ConfirmOrder($id, 'email')));

The split that makes it work is that the command holds data and the handler holds dependencies. A command carrying a Mailer cannot be serialised onto a queue; one carrying an order id and a string can be, and can also be logged, replayed and diffed against the one that failed. That constraint is the whole discipline — if the operation needs the current user, the user id goes in the command, not the session. The cost is two classes where there was a method, which is far too much ceremony for something that will never leave the request. The test for whether it is worth it: could this plausibly be triggered by a console command, a webhook, or a retry? If not, leave it in the controller.