A
A
Alexander Evgenievich2015-12-08 15:38:13
symfony
Alexander Evgenievich, 2015-12-08 15:38:13

How to correctly organize the Symfony2 service architecture to support multiple versions of the API at the same time?

Hello.
I looked at this material on how to organize the work of several APIs at the same time. The most suitable method for me is the transformation of Request requests to form the data structure necessary for the service. Business logic services are always the same and change only towards the current api version. The question remains only how to organize such a structure in PHP, and in particular, in Symfony?
Those. as I imagine it:
- the transformer in the controller will be used, when receiving the date from requesta, the transformer validates this data for the api version that this controller processes, then, if the data is correct, the transformer transforms this data into those that will be necessary for work our service.
- and a validator will be used in the service that validates the data, already directly in the service.
For example:
In the first version of the API, when creating a profile, we used the first_name, last_name, patronymic fields.
In the second version of the API, we only use the name_info field, which contains the full name information. Accordingly, the service will work with us with the new version and will accept an array with the $data['name_info'] key as input, which will validate.

class ProfileFactory
{
    public function create(array $data){
        // throwing some ValidateException 
        $this->validator->validate(CreationValidateRules(), $data);

        $profile = new Profile();
        $profile->setNameInfo($data['name_info']); // Вот тут смущает то, что сервис и валидатор знают о ключах массива отдельно друг от друга
       //.... some code
        return $profile;
    }
}

In the controller of the new version, we will simply pass an array to the service,
class ProfileControllerApi_2{

    public function createProfile(Request $request){
        $data = $request->request->all();

        $profile = $this->get('profile_factory')->create($data );

       // ... etc.
    }

}

and in the old one, we will pass the array through the validator and transformer so that the array meets the new requirements.
class ProfileControllerApi_1{

    public function createProfile(Request $request){
        $data = $request->request->all();
        //throwing some ValidateException
        $transformedData = $this->transformerHandler->transform(ProfileCreatingTransformer(), $data);

        $profile = $this->get('profile_factory')->create($transformedData );

       // ... etc.
    }

}

But there are such moments as validation errors. For example, I send data to the old API, but the transformer incorrectly formed an array for the service and added nameS_info instead of name_info . Should the service throw a validation exception here (the service also validates), because the client that sent the data to the server sent valid data for the old version, and this is a transformer error. Or divide exceptions by type - for validation exceptions inside the service - throw ServiceValidationException , and for exceptions inside controller validators just ValidationException ?
I apologize for the confusion, I hope I explained it more or less clearly, it’s just messy in my head, I want to do something more correctly.
Is it possible to use such a structure, or are there other, more optimal approaches?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
S
Sergey, 2015-12-08
@banderos120

The question remains only how to organize such a structure in PHP, and in particular, in Symfony?

imagine that for each version of the API we have our own controllers, our own controller actions, etc. Somehow like this. That is, the controller takes data from the request and asks the service to do something. As you described, the service is always the same and it is always up to date, but must take into account backward compatibility. That is, you cannot just take and add a required field.
And the controllers are already sorting it all out. Next, we can simply eliminate duplication and make things easier to work with. It can be just two routes for one action if we have no changes there, or we can also make separate handlers for deserialization, etc.
If we talk about architecture, the ideal option is hexagonal architecture. Then we will perceive different versions of the API as simply different adapters to the application layer.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question