What is the common API endpoint architecture using DTO? Hey I always used to create some pretty messy endpoints and I decided that it's about time to start writing cleaner code, I'll provide an example of how would I design an endpoint and I'll be  thankful for any insights why is it good or bad practice to do it like this also I'd be more than happy if you provide your own tweaks with explanation why would you do it like that.
Scenario: user post endpoint
Pathname: /api/user
Method: POST
Controller:
class UserPostEndpoint extends AbstractController
{
    private UserPostHandlerInterface $handler;
    private SerializerInterface $serializer;

    function __construct(UserPostHandlerInterface $handler, SerializerInterface $serializer)
    {
        $this-&gt;handler = $handler;
        $this-&gt;serializer = $serializer;
    }

    public function handleRequest(Request $request): Response
    {
        try {
            $userPostRequestDTO = $this-&gt;serializer-&gt;unserialize($request-&gt;getContent(), UserPostRequestDTO::class);
            $response = $this-&gt;handler-&gt;handle($userPostRequestDTO); // &lt;- ApiSuccessResponse(statusCode = 201)
        } catch(SerializationException $e) {
            $response = new ApiErrorResponse('Invalid request content provided', 400);
        } catch(ApiRequestContentValidationException $e) {
            $response = new ApiErrorReponse($e-&gt;getPlainMessage(), 400);
        } catch(\Exception $e) {
            $response = new ApiErrorReponse('Internal server error', 500);
        } finally {
            return $response;
        }
    }
}

Handler would contain some basic things like: invoke createUser in UserFactory, persist the entity with repository.
ApiErrorReponse: message, statusCode
ApiSuccessReponse: UserPostResponseDTO, statusCode(201)
Would you add / change / remove something from the given example to make it more &quot;correct&quot;? Thanks for the insights 
EDIT
After a few comments from @slepic I refactored the code into this
class UserPostEndpoint extends AbstractController
{
    private UserPostHandlerInterface $handler;
    private DataTransformerInterface $dataTransformer;

    function __construct(UserPostHandlerInterface $handler, DataTransformerInterface $dataTransformer)
    {
        $this-&gt;handler = $handler;
        $this-&gt;dataTransformer = $dataTransformer;
    }

    public function handleRequest(Request $request): Response
    {
        try {
            $userPostRequestDTO = $this-&gt;dataTransformer-&gt;toRequestDto($request-&gt;getContent(), $request-&gt;headers-&gt;get('Content-Type', 'application/json'));
            $userPostResponseDTO = $this-&gt;handler-&gt;handle($userPostRequestDTO);
            $response = new ApiSuccessResponse($userPostResponseDTO, Response::HTTP_CREATED);
        } catch(SerializationException $e) {
            $response = new ApiErrorResponse('Invalid request content provided', Response::HTTP_BAD_REQUEST);
        } catch(ApiRequestContentValidationException $e) {
            $response = new ApiErrorReponse($e-&gt;getPlainMessage(), Response::HTTP_BAD_REQUEST);
        } catch(\Exception $e) {
            $response = new ApiErrorReponse('Internal server error', Response::HTTP_INTERNAL_SERVER_ERROR);
        } finally {
            return $response;
        }
    }
}

I hope this is more appropriate
How about:
class PostController extends AbstractController
{
    public function store(PostStoreRequest $request): Response
    {
        $dataStruct = app(PostDataStruct::class)-&gt;fill($request-&gt;validated());
    
        return app(UserPostHandlerInterface::class)-&gt;createPost($dataStruct);
    }
}


PostStoreRequest validates data
PostDataStruct stores contents and has a method that fill all the data for you.
Your code looks a bit like Laravel so I utilized Laravels app container. This way you will have an easier time mocking objects in phpunit.
Within Kernel file forException Handlers, you can define how certain exceptions are handled. i.e. if you call throw new SystemExceptionError('my message') - in the Kernel file state that SystemExceptionError should have HTTP status 500.