Типичное нарушение ACID в symfony проекте

Столкнулся с частым явлением нарушения ACID в symfony проекте.

    public function batchCreate(
        PurchaseOrder $purchaseOrder,
        array $nomenclatures,
        AdUser $attachingUser
    ): void {
        $this->em->beginTransaction();

        try {

            foreach ($nomenclatures as $nomenclature) {
                $this->createOrIncreaseQuantity(
                    $purchaseOrder->getId(),
                    $nomenclature->getId(),
                    $nomenclature->getQuantity(),
                    $attachingUser
                );
            }

            $this->recalculateFactAmountForPurchaseOrder($purchaseOrder->getId());
            $this->purchaseOrderDocumentAmountService->recalculateAllDocumentWithOutDiscrepancy($purchaseOrder);
            $this->em->commit();
        } catch (Throwable $e) {
            $this->em->rollBack();
            throw $e;
        }

        $this->producer->send(new PurchaseOrderUpdatedAsyncMessage($purchaseOrder->getId()), $purchaseOrder->getGuid());
    }

На высоко нагруженном проекте в данной функции будут регулярно возникать самые разные интересные ошибки. Возможно программист писавший это был окрылен php 7.3 или не понимал, что творит. А возможно эта функция деградировала вместе с кодовой базой.

Так что так?

А начало транзакции с участием объекта извне. $purchaseOrder может существовать как объект, но уже не быть как запись в СУБД. Правильно в этом случает в функцию передать $purchaseOrderId и уже внутри получить $purchaseOrder для манипуляций. Должно получиться, что-то типа

    public function batchCreate(
        int $purchaseOrderId,
        array $nomenclatures,
        AdUser $attachingUser
    ): void {
        $this->em->beginTransaction();

        try {
            $purchaseOrder = $this->purchaseOrderRepository->find($purchaseOrderId);

            if (!$purchaseOrder) {
                throw new InvalidArgumentException("Данной закупки уже нет");
            }

            foreach ($nomenclatures as $nomenclature) {
                $this->createOrIncreaseQuantity(
                    $purchaseOrder->getId(),
                    $nomenclature->getId(),
                    $nomenclature->getQuantity(),
                    $attachingUser
                );
            }

            $this->recalculateFactAmountForPurchaseOrder($purchaseOrder->getId());
            $this->purchaseOrderDocumentAmountService->recalculateAllDocumentWithOutDiscrepancy($purchaseOrder);
            $this->em->commit();
            $this->producer->send(new PurchaseOrderUpdatedAsyncMessage($purchaseOrder->getId()), $purchaseOrder->getGuid());
        } catch (Throwable $e) {
            $this->em->rollBack();
            throw $e;
        }
    }
Written on July 3, 2022