1

Having built a couple of moderately complex API's - both in Laravel - I appreciate the value of transforming data before sending it in a response.

What I am struggling with is how this data should be handled when it comes back, so for create and especially update methods. Should it be transformed (but in reverse)?

For example, say we have the following Task transformer:

class ContractTransformer extends Transformer
{
    /**
     * @param $task
     * @return array
     */
    public function transform($task)
    {
        return [
            'id'           => $task['id'],
            'title'        => $task['title'],
            'description'  => $task['description'],
            'due_date'     => $task['end_date']
        ];
    }
}

Note that the value for the column 'end_date' in the database is returned as 'due_date' - a primitive example of returning the column name in a more user friendly way.

Say we have a user consuming this API and building the front end with Angular/React or another framework, they receive a value for 'due_date' and therefore surely they would expect to send it back (for an update operation) as 'due_date' rather than 'end_date'?

Then we have the problem (especially for large objects with many fields) of having to do something like the following:

$task = Task::find($id);
        $task->title = $request->input('title');
        $task->description = $request->input('description');
        // and so on...
        $task->save();

Rather than using:

$request->all();

And it means if column names change we have to update all the update methods too.

Is there a good way to handle this? Should the data be 're-transformed'?

0

Short answer: yes, when interacting with the UI (or the database or any other outside system for that matter), you should do basically what you have outlined here.

In a non-trivial web application, you'll usually have at least three major subsystems:

  • some kind of front end that deals with user interaction
  • a business layer that implements whatever the application's purpose is
  • some kind of data access layer

Each of those components will generally have different needs for how your data is structured. To keep these systems separate (and therefore maintainable), none of the systems should even be aware of the way any of the others represent their data.

Instead, they communicate by passing simple DTOs between each other, just like you showed in your examples. So for every action, you define a request-DTO and a response-DTO. On both sides of the API, you convert them to whatever you need.

This may seem like a lot of work, but consider:

  • You'll need to do input validation anyways, and these tasks go well together
  • When you change the internal representation of one system, all you need to do is update the conversion methods of that system - rather than everything everywhere.
  • You can properly encapsulate your state in the domain model, i.e. have a Task class rather than an array that is accessible by everyone. And you might have multiple objects from which your DTO is constructed, so it's often not a 1:1 mapping.

All of this being said, if your application is simple, and you end up essentially just converting one array to another, you might not benefit from this strict separation.

Final thoughts:

  • Make the API consistent: if it's called due_date going out, then it should also be called due_date going in - in the same format, etc.
  • If you're often converting very large data structures, it's possible that there is a better design with smaller data structures. For example, instead of having an UpdateTask action that changes 30 values, there might be value in replacing this with many smaller actions, like UpdateTaskDueDate etc. This really depends on your requirements...

This article may also be interesting to you - the last part of it deals with how and why to use DTOs rather than whatever data structure you happen to have in your business layer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.