S
S
Sergey Ilichev2022-02-11 20:49:54
PHP
Sergey Ilichev, 2022-02-11 20:49:54

Should I add a decorator to handle exceptions?

Hello.

There is a consumer class (handler) that receives messages from the queue. It contains a try catch block, service methods are called inside, these service methods can throw exceptions, these methods also access repositories, such as creating records in the database and updating, respectively, repositories can also throw exceptions of the PDOException type. All these errors are caught in the consumer (handler), that is, at the very top. I was offered to move exception handling from the consumer to a separate decorator, which, accordingly, will already pull the consumer (handler) method that works with services. That is, logically, the decorator in this case will become a consumer, since this will be the entry point of the message. Is such a layer necessary? How does this make the code more convenient? For now, I think this is an over-complication.

Here is the code

public function __invoke(OfdTicketMessage $ofdTicketMessage): void
    {
        $this->logger->info(
            'OfdTicketMessage was received',
            ['data' => $ofdTicketMessage->toArray()]
        );

        try {
            $this->ofdTicketService->create($ofdTicketMessage);
            $this->orangeApiOfdTicketService->createTicket($ofdTicketMessage);

            $this->ofdTicketService->update(
                $ofdTicketMessage,
                [
                    'processed' => true,
                    'statusCode' => OrangeApiResponseStatus::RESPONSE_CREATED
                ]
            );
        } catch (AlreadyCreatedException|AlreadyProcessedException|ConflictException $e) {
            $this->logger->info(
                'Need to ack message because: ' . $e->getMessage(),
                ['ofdTicketId' => $ofdTicketMessage->getId()]
            );

            throw new  UnrecoverableMessageHandlingException($e->getMessage(), $e->getCode());
        } catch (OfdOrangeApiException $e) {
            $this->logger->error(
                'Ofd orange api error: ' . $e->getMessage(),
                ['ofdTicketId' => $ofdTicketMessage->getId()]
            );

            $this->ofdTicketService->update(
                $ofdTicketMessage,
                [
                    'error' => 'Ofd orange api error: ' . $e->getMessage(),
                    'statusCode' => $e->getCode()
                ]
            );
        } catch (TransportExceptionInterface $e) {
            $this->logger->error(
                'Can\'t send request to ofd orange api: ' . $e->getMessage(),
                ['ofdTicketId' => $ofdTicketMessage->getId()]
            );

            $this->ofdTicketService->update(
                $ofdTicketMessage,
                [
                    'error' => 'Can\'t send request to ofd orange api: ' . $e->getMessage(),
                    'statusCode' => $e->getCode()
                ]
            );
        } catch (\Exception|\Throwable $e) {
            $this->logger->error(
                'Something went wrong: ' . $e->getMessage(),
                ['ofdTicketId' => $ofdTicketMessage->getId()]
            );

            $this->ofdTicketService->update(
                $ofdTicketMessage,
                [
                    'error' => 'Something went wrong: ' . $e->getMessage(),
                    'statusCode' => InternalExceptionStatus::INTERNAL_ERROR
                ]
            );

            throw new  UnrecoverableMessageHandlingException($e->getMessage(), $e->getCode());
        }
    }

Answer the question

In order to leave comments, you need to log in

2 answer(s)
M
Michael, 2022-02-11
@Akela_wolf

Depends on what logic is executed in the exception handlers. If there is just logging of exceptions, which is in no way connected with the logic of the consumer, then it is quite justified to separate this logic. But you can not separate. It all depends on the desire to reuse the consumer and the decorator. It also makes sense to separate such independent things to make unit testing easier.
But if there is more complex logic in exception handling, for example, related to recovery after an error, intertwined with consumer logic, then it will be difficult to separate such logic and it is not always possible to tell whether this should be done without seeing the code.

A
Anton Shamanov, 2022-02-12
@SilenceOfWinter

It's not a decorator, it's a proxy. I would rather do error logging in the exceptions themselves. If the request to api may fail due to api or network server problems, then it is better not to throw an exception right away, but to try to make a couple more attempts, and only then throw an exception.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question