The Action Pattern Is Key to Clean Code
Chapters11
Introduces the action pattern as a simplified form of the command bus and its origin in Laravel.
The action pattern merges command and handler into a simple, reusable class, and is especially powerful in Laravel when you keep HTTP concerns out and enable cross-layer reuse.
Summary
Nuno Maduro champions the action pattern as a streamlined evolution of the command bus, tracing its origins to Taylor Otwell’s self-handling command bus and highlighting its resurgence in the Laravel ecosystem. He explains that an action combines the payload and the handler into a single class, making it easier to reuse across layers like console and jobs without HTTP-specific logic. The pattern emphasizes validated input from a form request, with data passed to the action as a plain array rather than HTTP objects to support non-HTTP contexts. Maduro stresses the importance of not validating inside the action and advocates for wrapping multiple database actions in a transaction to ensure atomicity, especially when external calls (like sending emails) are involved. He demonstrates how actions can be composed—injecting other actions (e.g., creating an activity when a user is updated) and using nested transactions in Laravel. The video also discusses practical considerations: when to use arrays versus DTOs, where to place actions in the project structure, and whether actions are preferable to events and observers for clarity and explicitness. Finally, Maduro invites debate on topics like action placement, querying via actions, and the trade-offs between actions and more traditional patterns, inviting comments and feedback from viewers.
Key Takeaways
- Action pattern merges the command and its handler into a single, simple class, making it easy to reuse across layers like HTTP controllers, console commands, and jobs.
- Always validate data in the form request; the action should receive only validated data (as an array) and should not perform HTTP-related logic.
- Wrap multiple database updates or external operations (like sending emails) in a DB transaction so failures can roll back the entire action.
- Actions can inject other actions (via dependency injection) to compose complex behavior, such as updating a user and creating an associated activity in a single transaction.
- Decide between using an array with PHP array shapes or DTOs for action attributes; arrays avoid extra files, while DTOs offer stricter typing and structure.
- HTTP concerns must stay out of actions; anything HTTP-related should be handled in the controller, not inside the action’s body.
- Projects can keep actions in a flat actions folder (e.g., create, update, delete) without subfolders, even in large codebases with 100+ files.
Who Is This For?
Essential viewing for Laravel developers exploring clean architecture patterns, particularly those considering adopting or refactoring toward the action pattern to improve code reuse, testability, and cross-layer consistency.
Notable Quotes
"The action pattern is a simplified version of the command bus."
—Nuno introduces the core concept and how it differs from the traditional command bus.
"You are sending the validated information. And also don't worry if the information is not valid you won't even arrive to this point because Laravel automatically bails the request off if this request is not valid."
—Emphasizes the separation of validation and action execution.
"One of the most sexy things about the action pattern is the fact that you can reuse actions."
—Highlights cross-layer reuse and composability.
"If any of these processes went bad, you want to roll back the entire process."
—Explains the importance of DB transactions with actions.
"HTTP concerns are not inside the action; if you eventually need to use the HTTP layer, just do it outside of the action on the controller."
—Clarifies boundaries between HTTP layer and business logic.
Questions This Video Answers
- What is the Laravel action pattern and how does it compare to the command bus?
- How do you implement transactions when using actions in Laravel?
- Should I use DTOs or arrays for action attributes in Laravel?
- Where should I place action classes in a Laravel project and do I need a separate queries folder?
- Why might I choose actions over events and observers for certain workflows in Laravel?
Laravel Action PatternLaravel command busform requestsdependency injectiontransactionsDTOs vs arraysHTTP concerns in actionsLaravel transactionsevent vs action patternscode organization
Full Transcript
The action pattern is my favorite pattern ever. So on this video, we're going to talk about what it is, what is the story behind it, and why do I love it so much. First of all, I want to tell you when I did first learned about the action pattern. It was literally 10 years ago, and Taylor Otwell called this thing the self-handling command bus. Actually, the action pattern is a simplified version of the command bus pattern. Back in the days, people used to use the command bus pattern which is literally having the command which is the payload of data and the handler which is the way you handle the command.
Now in theory with command bus you can have multiple handlers but in practice you just kind of just have one. So this is where the action pattern have emerged. Basically the action pattern is the command and the handler merged together in a simple class. Even though people talk about the action pattern since 10 years ago in the recent years, literally like 3 2 years ago, it got really popular on the Laravel world. On this video, we're going to see why. All right, let's skip the and go straight to the point. What exactly means using the action pattern in your Laravel project?
It means the following. On every single request method, you have a form request which is responsible to validate the request layer. Then as a second or third argument depending if you are using model ROM binding or not you would have the action itself. The action is a simple class which takes the validated payload to perform an action in your system. It could be mutate the user it could be update something whatever happens inside of that class itself. Now super important okay insanely important when you are calling the action you never send any HTTP concern on it.
meaning the following. You are updating a user. Therefore, you are sending the user resource as an argument as well to the action. This is actually super important that you don't send any HTTP concern like the form request itself, but instead you send an array of data because you might want to reuse this action on other layers like the console layer, the jobs layer that don't have any HTTP concern. Now, about the form request, you need to make sure you validate the data all the way through. Remember the action itself won't validate any data. It will assume that the data has already been validated.
As such, it's important that you've just validate the entire thing. The email needs to be required, needs to be a string, needs to be lowerase, needs to be an actual email, needs to have a maximum of 250 and needs to be unique on the system. This is the place where you validate the data, not within the action itself. Now mentioning the obvious but of course to the action itself you send the validated information. So you always make sure you call validated to only get validated information. And also don't worry if the information is not valid you won't even arrive to this point because Laravel automatically bails the request off if this request is not valid.
Now I know exactly what you're thinking right now. What is this attribute current user? This is actually a very sexy attribute my friends which does the following. It just injects automatically the user that is logged in on the system. Meaning that here I'm getting the user that is logged in on this web application automatically which is in my opinion pretty cool. You don't have to do this kind of which is doing user request user thing which is really annoying. So you can just literally get the user automatically through the arguments of this controller which is in my opinion a very sexy thing.
Let's go straight to the meat talking about what exactly an action contains inside. That's a very good topic. Let's approach that thing. So within the action, you are going to actually perform business logic. What it means updating a user. Well, in your business logic, updating a user means going to the database and effectively mutate the DB rows. But it also means another thing which is notifying the user that his email may have been changed while updating this user. So this is what we like to call business logic. Now there is actually a bug on this action code.
There is actually something that can happen on this action code which is a bug. Can you spot it? And exactly you got it right. What we are missing here is a DB transaction. Now why a DB transaction is important when talking about the action pattern because when you mutate your system you want to make sure you went all the way through that we have mutated all the parts in our system. So if you are doing multiple updates, if you are equally sending an email or if you are doing something else in the process, you want to make sure that if any of these pieces fail, you just roll back the entire thing.
Okay? So every single time you are performing multiple database actions or you need to reach an external API potentially for example, you want to make sure that if any of these processes went bad, you want to roll back the entire process. You are just like you were in the beginning. Okay. So here for example, if we fail sending the email to the user, we don't want to mark this update as done, we want to roll back this to the previous state and notify the user that something went bad with the update so he can retry and effectively receive the email on his inbox.
Now my friends, one of the most sexy things about the action pattern is the fact that you can reuse actions. This is like the most sexiest thing in the planet. Okay, now why this is important? Imagine that you have a timeline of activities. Okay, your platform has a timeline of activities. You can create your own activities by just saying, "I'm going to eat some pizza tonight." But also, the fact that you updated your profile will automatically create an activity just like LinkedIn. Okay. Now, what you can do here is that you can go to the update user action and say, I want this update user action to also create an activity every time I update an user.
So, you inject that through dependency injection. Laravel will automatically inject that thing for us. And then you go all the way down and within the DB transaction, you just call the create activity with the user updated. And if something goes wrong here, the transaction will be reverted. Okay, you can use nested transactions. Laravel handles that perfectly and it really just works. I love this stuff. All right, one question you may have is why are we using an array here instead of a DTO, which is just a regular class that encapsulates state. That's a very good question.
I have not decided yet if I want to use a DTO all the time or if using an array for simple stuff is enough. I'm going to explain you why. So on PHP we have this thing called it array shapes which is totally supported by PHP stand. And what it does is that you can literally define the required shape of the given array. So if type safety is a thing in your project, it can analyze the given attributes to this action and make sure this given keys exist and exist with a given type. However, some people defend that it's just better to use DTOS here.
And it's legit. Honestly, using DTOs is equally good. However, what I've detected over time is actually having a DTO for every single action attribute. It can be a little bit too much in terms of extra files in your project. But I want to hear from you. Write in the comments below what do you think. Again, super important that within this action, you don't use anything that is HTTP only. It's kind of cool that you can reuse these actions within other layers like the console layer or the jobs layer. And for that, you cannot have any HTTP related code within the body of the action.
So if you eventually need to use the HTTP layer, just do it outside of the action on the controller itself. And I actually have a very good example for you. Let's assume, for example, that the user wants to destroy his account on your platform. Now, destroying an account involves potentially multiple operations, which you can handle within the action itself. Deleting the user history, deleting the user from the DB, all of that you can do from the action itself. Even sending an email to the user that his account got deleted. However, you also want to log out the user from your system.
log out the user invalidate the session and all that stuff because that stuff is HTTP only is an HTTP concern you have to do it at controller level now very important question in to finalize the video where do you place those actions I'm going to tell you something that a lot of people won't agree on people think about subfolders they think about the different modular kind of crap I'm going to be honest I just place all my actions within my actions folder they stay there like a flat folder which just has create create and then update, update, update, update, and then delete, delete, delete, delete.
I have projects with more than 100 files right here, and it's no problem at all. Now, you may have a question here, which is well, for updating, for creating or deleting something, you can use actions indeed. But how about things like doing a query? If you need to fetch a given product from the database, do you use an action? If you need to fetch all of them, do you also use an action? Well, I have seen multiple approaches out there. I have seen people who have this list products action which is something that aggregates a query but I also have seen people who have created a queries folder where does have things like list products or potentially get product I have seen kind of everything I've also seen people who just have this inline query directly on the controller let me know what you do on the comments below now very important very important why would you choose the action pattern instead of something like events and observers I'm going to be honest with you I have developed apps in this two different ways and in My opinion, the action pattern is way more explicit.
You know exactly what's happening. You can see if you want to update the user, you can just go to the action itself. You know exactly what's happening. And with events, you are just firing stuff. You don't know what's reacting over time. It just gets messy because you want to update the user using a migration for example and suddenly you are sending emails to everyone. It just becomes a messy to understand what's happening and when it's happening. And that's it about the action pattern. We can go way deeper than this stuff. Let me know if you want to see that.
Also, let me know what you think about the action pattern. I want to see your opinion on the comments below. If you enjoyed this video, make sure to click on the like button, okay? Super important. But you also subscribe the channel. I would really appreciate. Love you all and see you guys next time. Peace out. going to do.
More from nunomaduro
Related Videos
Get daily recaps from
nunomaduro
AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.






