Should you use DTOs in Laravel?
Welcome to No Compromises. A peek into the mind of two old web devs who have seen some things. This is Joel.
And this is Aaron.
There's different flavors of PHP that we see out in the wild. And PHP started as a super dynamic, loose, flexible language, and now we have strict types and features that you can opt into.
And I would say we probably tend a little more towards the strict side than the loose side for various reasons. You know, we're not going to get into that today. But we use tools, like Larastan and Rector, and things like that.
But one thing I was thinking about recently that I noticed we don't use, and I do hear people really enjoying using, are DTOs, Data Transfer Objects. Where instead of passing around the giant array with three or four things in it, or a bunch of parameters that these nice tidy objects, and all that.
So I just wanted to bring it up, like, should we do this? Why don't we do it? And I want to get Aaron's view on this.
Okay. I can think of a lot of our projects in the past, and I'm thinking about Data Transfer Objects and if we've used them. And I don't necessarily think that we have reached for them very often.
I know that we've done a hybrid where it's like there was so many different parameters that need to go into a method call that we've created a object that had all those parameters set to default, since you could pass in that object. I think it was a complicated search or something like that.
Where we say, we get from the user interface a bunch of different search things, manipulate them, put them in this object instead of passing in 27 different parameters to method, and you pass that and you get to results.
I know we've done that, but the only other time that we work with objects is when Eloquent builds them for us, or if we're using the database facade and it returns a standard object.
Sure.
But I don't think we ever work with those Data Transfer Objects. I don't know if we have a lot to pull on for examples of how we use it, but it is a good topic.
Where I think about it, and I don't remember if it's where this question originated from, but one of the places I think about it is, we do like using Form Requests, right?
Mm-hmm (affirmative).
Some developers might see that as unnecessary files and ceremony. Like, "You're going to validate like three fields, man. Just put that in the controller." But we would still make a Form Request object for that, and then we would access the validated data coming back out of that Form Request.
And I know there's packages and things that will add a layer on top of that. Where now, instead of validated just giving you back an array of mixed type, it's giving you back a typed data object.
Where this field is a date, and this field is a boolean, and this field is... You know, if it was an enum or something, it's actually cast to that type without having to do it. And that kind of appeals to me.
Well, we already have that, I think, with the safe method on those requests now in Laravel. That's built in, you don't have to have a package. I understand what you're saying though. Having a sort of encapsulated way to deal with data that comes in, and then having a predictable way to work with it.
Right. I think one of the other use cases I hear that people like, is you can also share them, right? So, maybe if you have an API layer and just a traditional HTTP or form layer, and they're the same shape, well now you can reuse that detail.
But again, I don't feel like we run into that a lot. Even when you have those two layers, they're usually just a little bit different.
Yeah. The places where I've seen them used successfully and with intention has been when you want to normalize multiple different third-party APIs into your system. For example, you have incoming data from... I'm going to just make something up.
But let's just say incoming data from Twitter and incoming data from Facebook. One of them comes in in one shape and one comes in the other and in your internal system, once you receive something from them or you've retrieved it, or whatever, then you have a really complex process that you do with that data.
In that case, it might make sense to say, "I've retrieved something from my Twitter API, which came all as one big object and I modified that and put that into my DTO." And then I send that out and then when I call Facebook, I actually need two HTTP requests to get all my data.
So it goes and does two different things, then builds that DTO and comes back. Out of those services, maybe I even use an adapter or a strategy pattern to determine which one I load in at runtime.
But I always get a predictable Data Transfer Object out of that, of someone else's data in a data format that I can predict and work with.
Yeah. I think of that too as a form of... Like enforcing a boundary too. We would do that even if you don't have multiple types. Even for Stripe, we typically don't let that Stripe object that gets returned bubble all the way through your app.
You kind of have a layer where you take their object and now it's like, "Okay, now here's the data I care about from it." Maybe in that sense, we have used DTOs more than I'm giving us credit for. Just not in the controller layer, but more in that service layer.
One of the things I think that DTOs had given us in the past... I mean, it still gives us now, but with the tooling, was having my IDE being able to recognize all the different fields of something. But we can do that now with good PHPDoc syntax and say if I'm going to pass an array.
So I guess that was my concern too, is maybe even when you're transferring array back and forth, you have three fields. But if you're passing it back from a service, you don't know what those three fields are. If you're passing back a DTO with public properties, you always know what those are going to be because they're always public properties.
But if you're going to use your IDE and then you use your PHPDoc return types with the array keys defined in there. You're going to get a lot of that stuff anyway and not have to introduce that extra layer of code, and logic, and memory, and stuff.
I guess in that case, I'm not against them but I would say that what is the reason you're using it for? And is there an alternative way that's already built in that we have available to us that gets you that same result?
Yeah. In fact, you just hinted at something that didn't fully click for me, but as another reason, I think form requests are part of the Laravel framework. They're in the docs, there's a conventional way to use them.
Whereas a DTO, I mean, on the one hand it's just a PHP class, but it's sort of like you kind of have to design how you are going to use them. Well, how are you going to name them? What director are you going to put them in? It's not some weird thing you're doing, but it's also not necessarily part of the conventional way of using Laravel for most applications.
At least the ones that I see. So, we're not resisting this because we're afraid of adding more files. That much is clear. The other thing I thought of, too is with the strict types and with things Larastan, we also don't go crazy with high levels.
We usually stop at 5, maybe 6. And I think that's another area where the DTOs get you out of some trouble. Is, if you're trying to do level 8 or 9, it does make that a little bit easier to sort of be way more explicit about that stuff.
But we don't typically go that high in an application so maybe that's another reason we don't really feel a strong need to pull them in.
I guess it makes sense. We want to look at DTOs like we look at everything else. Action, patterns, services, events, all those different things. It's a great tool for our toolbox. But think about why you're going to use it, think about the complexity you're adding on and is it the right tool for the job?
Or is it the right tool to solve the problem that you think you have? Or maybe you don't even have and you just are imposing on yourself using too high level of a PHPStan?
So I am looking out my window right now and I see something in someone else's window and I don't know exactly what it is. And there's no reason for me to know what it is, but I see it all the time and now my brain is making stories up out of it.
It's some sort of weird shape and the way it looks is a tongue, like a emoji tongue in their window, sticking... but that's it. And it's diagonal and it has some writing on it. I don't know what it is.
How big?
About two-thirds of the size of the window.
Okay.
But it's far enough that I can't see the writing. And I guess I need advice. Is it weird to go out on my balcony and take out my camera and take a picture of that person's window and then enlarge it and try to figure out what that says?
Or do I just know that there's just some things in life I'm just not going to know what it is, and that's fine?
First of all, I do think it's a little weird-
And a follow-up. Should I do it in the dark no one sees me?
That's where I was going to go. Is like, I would just wait until, you know... Perfect world, it's dark, but they have their light on so it's like lit up better and you can see it. But then I would also caution you.
Sometimes the reality, like if you find out what it is that's going to be way boring than whatever you invent in your head. So it could be a letdown too.
Or it could be way worse. It could be, "Aaron, you're the worst." And I'm like, "I can't believe this says this this entire time."
Well, that's a lot to consider, but we can help you out with this. We can help you with decisions like maybe should you use a DTO or definitely not.
You're already making the decision. But we are available if you'd like an architecture review or just a pairing session to kind of think through some of these big issues. Head to masteringlaravel.io and book a call with us.