Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

EnterUsername's avatar

DTOs can be redundant, prove me wrong!

Hi,

I recently read spaties "beyond crud" blog post and everything from the folder structure, to the actions and state made sense to me, but the DTO's didn't.

If we look at this quote

"First of all: we already established that DTOs are the entry point for data into the codebase. As soon as we're working with data from the outside, we want to convert it to a DTO"

That tells me that all data (3rd party APIs, internal APIS, everything!) should use DTOs.

So when I build an API-first laravel app I usually do this :

UserController

<?php

class UserController extends Controller
{
    public function store(StoreUserRequest $request)
    {
        $this->authorize('create', User::class);
        return User::create($request->all());
    }
}

But with DTOs, I feel like I just add a bunch of redundant code for no reason

UserController

<?php

class UserController extends Controller
{
    public function store(StoreUserRequest $request)
    {
        $userData = tap(new UserData)->fromRequest($request);
        $this->authorize('create', User::class);
        return User::create($userData->toArray());
    }
}

UserData DTO

<?php

namespace Domain\User\DTOs;

use Domain\User\Models\User;
use Illuminate\Http\Request;
use Spatie\DataTransferObject\DataTransferObject;

class UserData extends DataTransferObject
{
    public string $email;
    public bool $is_admin;
    public bool $is_moderator;
    public bool $is_worker;
    public bool $is_active;
    public string $name;
    public ?string $phone;
    public ?int $hourly_cost;

    public function fromRequest(Request $request)
    {
        return new self([
            'email' => $request->get('email'),
            'is_admin' => $request->get('is_admin'),
            'is_moderator' => $request->get('is_moderator'),
            'is_worker' => $request->get('is_worker'),
            'is_active' => $request->get('is_active'),
            'name' => $request->get('name'),
            'phone' => $request->get('phone'),
            'hourly_cost' => $request->get('hourly_cost'),
        ]);
    }

    public function fromModel(User $user)
    {
        return new self([
            'email' => $user->email,
            'is_admin' => $user->is_admin,
            'is_moderator' => $user->is_moderator,
            'is_worker' => $user->is_worker,
            'is_active' => $user->is_active,
            'name' => $user->name,
            'phone' => $user->phone,
            'hourly_cost' => $user->hourly_cost,
        ]);
    }
}

I can understand why using DTOs for external APIs are a good idea, but for my applications internal http requests that has an indirect relationship to to a model, I don't get it.

Here is how I compare them

DTO

  • Takes unknown data and make it known
  • Is a class

Model

  • Takes uknown data and make it known
  • Is a class

So when I want to add an attribute to my table, not only do I have to

  • add it to my validation
  • add it to my model ($fillable, $searchable)
  • add it to my policies
  • add it to one or more resources

I also have to add it to my DTO!?

"My point of view is that we should embrace the framework, instead of trying to fight it; though we should embrace it in such a way that large projects stay maintainable."

Taking this quote into account, DTOs on application is fighting the framework by adding redundant code.

I am confused, scared and sad. Can someone help me on this.

0 likes
4 replies
Snapey's avatar

You do what feels right to you.

DTOs are only really beneficial when passing data between classes

I would not use one just to pass request data to an eloquent model (the model itself is the DTO)

But I would not use request->all() at all....

1 like
oroalej's avatar

I like DTOs, it provide structure to my data especially when doing a lot of re-useable functions instead of just an Array and I don't need to re-validate everything. When I receive a DTO, I know that thing is validated and in correct format.

I know we can use the Model as DTO but I want to separate a SAVED data from TO BE save data.

martinbean's avatar
Level 80

@enterusername I don’t like this proliferation of DTOs recently. It’s not what the design pattern was intended for. Data mappers exist for converting data from one representation (i.e. an third-party API) to another (i.e. your application’s internal models).

It’s just another case where Spatie have taken something and then every one’s jumped on the bandwagon (see: when Spatie re-christened command classes as “Actions” and then every Laravel developer started re-factoring everything in their apps to “actions” even though the pattern had existed for literally decades before).

EnterUsername's avatar

@oroalej @martinbean Thank you for your answers.

I have since my post, learned more about the topic and DDD in general and I now understand its more than folder structures and pretty names.

And regarding spatie @martinbean. I totally agree. I have moved away from worshipping spatie and started following channels like https://www.youtube.com/@CodeOpinion/videos where he talks generally about "ideas", instead of framework/code specific stuff like the entire "Beyond crud"- post is about

3 likes

Please or to participate in this conversation.