As a solo developer, I tried to stock to all the canonical practices, but soon realized that I'm overcomplicating my life simply because "this is the way". And the more events, the more listeners and more complicated the structure would be. I had days when I was navigating mindlessly through controller->event->listeners->actions/services.... until I found my own way, and now I'm refactoring events and listeners into actions and life is beautiful again.
@JiahuaCui-h8t3 ай бұрын
This sparked a discussion within our team. One of the argument to this approach is that in a larger project, each listener often has many more than the 3 lines of code shown in this example and grouping all listeners logic together will result in a very bloated Action. More importantly, the code in the action is synchronous procedure meaning if one of them failed then the remaining won't execute whereas in the event and listener approach, you can simply make a directory and group the listeners together by event.
@2gbeh3 ай бұрын
True
@perdanjam2793 ай бұрын
Could catch exceptions to help with that. Could also argue that in many cases, you would want transactions and rollbacks if one thing fails, so having it in one file in that case is clearer. Your points are valid, but the main thing is that there are always arguments to both sides.
@joerushton8863 ай бұрын
You can and should split out actions into multiple actions, once an action starts to experience that "bloat" or when you find you need to re-use a subset of an actions logic in another action. You still retain the benefit, in this example, of being able to see all the side effects of marking a best reply, because you are invoking the logic explicitly from the MarkBestReply action.
@Pekz00r2 ай бұрын
@@joerushton886 Yes, that is what I do as well, There is nothing wrong with calling another action inside your action.
@ward75762 ай бұрын
Yea, I don't really get it either. Even more so when you don't use auto-discovery for event listeners, you can simply do array key-value mapping in EventServiceProvider, making it plain and simple as to what happens on certain event. It's not much work and provides more visibility to side-effects.
3 ай бұрын
First of all, the videos are looking amazing! I agree with you on that example, but I wanted to add to the conversation (which I find very interesting): I think two of the most important things when it comes to event-driven functionality are 1) the failure model and 2) coupling. First, I think that if you can't justify doing something (e.g doing something because of SOLID, or because a book said so) yourself, this is problem itself. More often than not you have relatively complex processes - IIRC, on Modular Laravel I use e-commerce as an example: checking out a cart isn't just processing a payment and creating a record on orders: there is updating inventory and shipment to begin with, but more complex systems might also deal with warehouses, reaching out to distributors, shipping from different warehouses, cashback systems, etc., and in these situations the isolation/failure and decoupling model provided by events makes a lot of sense. By having each process independent from one another it's easier to deal with coupling between subsystems, handling non-catastrophic failures or gracefully fail, fault containment, observability, etc. Events also map closely to business requirements - they often go "when X happens, do Z", and events are a perfect narrative factor for that. By inspecting the commands and events of an application you can get a very nice overview of its capabilities. All in all, I agree with you - when we reach that intermediate level, it's easy to want to apply all the fancy techniques and patterns. Indirection always comes with a cost, so when implementing something, I think a fair question is: can we justify *why* we want to implement it in such a way, and can we describe the benefits and downsides of that?
@webpulseify2 ай бұрын
I am going to follow this approach. Thank you for these great insights! Keep it up; we love your content, Jeff!
@zerthur3 ай бұрын
This idea of having a sort of "entry point class" that provides an overview of a given functionality, where you can easily see the flow based on the functions defined, is very clever. Coz in just one file, you get to see how everything is pieced together. Then, every other file after it, regardless if it's a mountain of code, we already have a pretty good idea of the path we're trying to navigate.
@znat53 ай бұрын
More content like this please, Jeff😊
@JordanHumbertodeSouza3 ай бұрын
This is such a great video. So much can be learned when you're not so SOLID minded and more like a "gut feeling" guy.
@victoronwe9063 ай бұрын
I always break the rules, I often focus on how readable, and maintainable the codes are. You simply nailed it
@TimGavin3 ай бұрын
Read a blog post by Freek a couple years ago about this; adopted it and never looked back. This is the way.
@ronssijei3 ай бұрын
Saaaame.
@bambamboole13 ай бұрын
100 with you. events are for comunication over boundaries like websockets / message bus / modules
@samldev2 ай бұрын
One more step that could prevent future headaches is throwing all the database operations in a transaction. Then you don’t find yourself manually fixing data in your DB when one part of the action fails
@Flankymanga3 ай бұрын
Actions seems to be a similar to concept or service classes. You extract the advanced logic grouped arround the same topic out of the controller to a dedicated class.
@mehdi-vl5nnАй бұрын
The point is, many developers don't even know what's going on. When you're working with Laravel, it's true that you're writing object-oriented code (since Laravel is based on object-oriented principles), but your design is usually data-centered, or in engineering terms, your application is based on structured analysis and design, and you have a set of transaction scripts The most you do is decoupling and cohesion. There's no discussion of domain modeling or being use case-driven, whether you believe in SOLID principles or not. The minimal flow in domain modeling works like this: your code requests something from the domain model, the domain model performs a series of tasks, and in the end, your code updates the database based on the changes mapped from the domain model. For example, in this video, we have some functions that handle what the domain should do, and others that handle what the application should do. Even instructors misuse the term ‘use case.’
@shaikhfoysal44263 ай бұрын
Great. I will do that to my project. This will help a lot. Thanks.
@popforall3 ай бұрын
You have just solved my team's issues Thanks for making coding fun!
@mohamadnasir64373 ай бұрын
I follow your video since codeigniter 2..thank you for your guidances
@ahmad-murery3 ай бұрын
I use event listeners when the action to be taken is optional or when I need to trigger external functionality that is not directly affecting my app workflow usually in some kind of general utility classes Thanks Jeff!
@khessamahmed71172 ай бұрын
after that amazing course of laravel we want another course about vuejs3 in laravel and livewire pls and thank you Laracasts for everything
@HkanAktas3 ай бұрын
I like code simplicity as much as the next guy. But it's another intermediate dev trap to think that our job as software developers is to implement the happy path and be done with it. The code _will_ face issues. The developer has to think about and write code for handling errors and doing them in a user friendly way (doesn't just mean showing a nice error message), keeping data integrity both by guarding against obvious problems and by thinking about recovery paths. If the app is anything but a simple todo or another learner's app, the code will get considerably more complex than this. This is *not* a jab at the video, but simply a reminder to everyone that what you see in the video is not the full story.
@netsudro3 ай бұрын
This really helps me refactor a messy project that is hard to understand every time I come back to it to make small changes or fix bugs. Even though I am the only person who build it, it still makes my brain hurt.
@techfuture-code-tv3 ай бұрын
This is awesome. Thank you sir. I learned this simplicity
@JT-mr3db3 ай бұрын
I tend to return '$this' from these side effect type methods so I can chain them. It reads better IMO, and saves some characters and a few semicolons.
@laravelpursuit2 ай бұрын
Great talk, thank you
@enjibby3 ай бұрын
This is fundamentally a very similar principle to state pattern, transactions, or even activity log. I prefer to use Job as a base class in Laravel for these classes so they can be dispatched (synchronously or asynchronously) and I normally try to include a validation step (to ensure that e.g. the reply instance is in a valid state to be marked as best) but the benefits of this structure are obvious in hindsight. Audit logging of the execution of Action classes are also much more user friendly rather than the standard practice of audit logging database update statements.
@natenatters3 ай бұрын
Great quality on your videos as always :D But I disagree 😅 This breaks on larger projects. On a larger projct, you need to mange this yourself: * Set the $reply->status('becoming-best') * Chain those 3 as jobs * Have a 4th job to "complete" the action and set the status as $reply->status('best')
@AbderrahmanFodili3 ай бұрын
you were right, this pissed me off 😅 i do use actions but for repeated actions only , and i use the event service provider to link events to listeners in order to not forget what tirgers what.
@MatteoPresot3 ай бұрын
I follow pretty much the same approach (move from initial listeners to actions/jobs/pipelines). However my epiphany was driven by implementing transactions and dealing with race conditions rather than thinking about how it would look a year from now 😅
@steenrabol23783 ай бұрын
One thing to remember is that time spent in an action is time a user is waiting
@shabxs3 ай бұрын
Thanks for the insights...
@brunoggdev63053 ай бұрын
Simply logical. Good stuff
@techietoons3 ай бұрын
I like this approach.
@bbbbburton3 ай бұрын
I'd suggest putting all 3 side effects in queued jobs. The API consumer (user in most cases) doesn't directly care about any of those 3 side effects. The thing they are doing is marking a reply as "best." The other things can be eventually consistent, by means of queue.
@adeonabule15173 ай бұрын
Depends on the importance of the side effects, for some use cases this might not be efficient
@princeani39293 ай бұрын
I agree especially with scalability. But to his point, this lets you know what actions are part of the process. You can choose to queue instead of run the actions. But you still get one source of truth.
@MarkSnape3 ай бұрын
Depends on whether the order of side effects is important. Your notification might need the result of adding the experience for instance. You can still use the action in a queued job.
@joebashour3 ай бұрын
Such a great insight!
@TLJAFAR3 ай бұрын
It's a good one I'm using laravel action package this kind of thing.
@techietoons3 ай бұрын
Please post some more such trap videos.
@dr.adam.nielsen3 ай бұрын
I really enjoy these personal opinion-based videos-thanks for sharing, Jeff! Does that mean you don’t typically use events/listeners for your own code within your Laravel apps (other then as a package API)? If you do use them, I'd love to see an example where the event/listener approach is more maintainable for you compared to placing code in services or actions. I'm also curious if that means you're not a big fan of the techniques proposed in the "Modular Laravel" course from Laracasts.
@yasseralhassan41883 ай бұрын
Absolutely logical
@gareth23972 ай бұрын
Is this significant different to writing a service? You could directly call the various processes or despatch some events or add a queue etc within the service.
@farazsalehi90343 ай бұрын
yeah .. if readability is our priority this approach is the best. Still, if you think those actions will use in other places in the application, event listeners are a better approach, in my opinion, whoever uses laravel to create applications he/she must know about the framework feature.
@cookiebinary3 ай бұрын
I have been using Laravel since version 3. I'm still using this pattern, I never liked the listener and events.
@phpannotated3 ай бұрын
I love this!
@SaiyanJin853 ай бұрын
I would like your opinion on event sourcing, it kinda enforce us to use the first approach. I don't mean on a microservices separated app. Thanks
@Jara47M3 ай бұрын
Hey! What editor or IDE he is using in the video? I like the clean design.
@MarkSnape3 ай бұрын
I've switched between both approaches. I like listeners for events that are triggered by an external resource, which is similar to your package example. Actions work better where the sequence is important. In your original code, if you wanted to notify the user that their XP had increased to nnnnn then its important that the increments XP code is executed first. In Laravel 11, the ability to order the listeners has been lost in favour of auto-discovery which probably requires juggling class names to set the order - fragile !!!
@kenjohnsiosan97073 ай бұрын
Hi Jeff, is it a good practice to reuse listener as opposed to grouping it as it is shown when refactored? thanks
@ericsan15613 ай бұрын
You still need an event listener to do an action after the async task is done. Example i have to log or update a status after the notification sent. If the notification failed to send i need to know.
@hiajayy3 ай бұрын
I follow the same approach.
@phusedallas35363 ай бұрын
My colleagues always tell me to say "dump and die" instead of "die and dump". I always wondered why I say that..just realised while watching this video that Jeffery Way spoiled me lol
@xristosgr5673 ай бұрын
I really like this way of coding, it's simple and elegant, but what if you need some kind of response from the action or truck any failures on the way?
@blessdarah12563 ай бұрын
You can use an event or queue if you must trigger so many other things.
@EnriqueMunoz-no1fq3 ай бұрын
Was going to ask the same thing
@MarkSnape3 ай бұрын
throw an exception in the action and use try-catch in the controller if you expect there could be errors.
@GeorgeDrakakis783 ай бұрын
this is the way indeed :)
@ticorj2 ай бұрын
So using a Service Class approach could the action class lead to a set of rules that is not present on the service?
@ReinhardtCagara3 ай бұрын
What would you consider non-primary actions / sideeffects?
@shofada3 ай бұрын
This makes sense.
@adelkazem86453 ай бұрын
Awesome thanks
@jonnyso12 ай бұрын
This right here is why I stopeed using Laravel. To be fair, mostly my fault, but it felt like everything in Laravel, even the stuff that I didn't write, worked like this. Juping through a bunch of files with that do a little thing to configure something, to find out how something works, its the way the framework is setup. These days I generally stack everything in the same place and when it gets annoying I refactor in a way that makes sense. PS: For this particular example though, I would just create a Subscriber.
@yadindominguez10272 ай бұрын
Hello, just one question since you do this, what stops you from putting it in the controller, wouldn't it be the same?
@louisevirtudazo28223 ай бұрын
Isn't it good practice to run these sides as queue? Where you could easily do using listeners.
@seeking76623 ай бұрын
Hi, what do you use for local development ? I need multiple version of PHP so I am using docker, but doing testing etc is bit harder.
@nishantbhorodia3 ай бұрын
Try using Laravel Herd
@2gbeh3 ай бұрын
I'd do the same except i'd move everything within the action handle method back to the invokeable controller. That's technically why they exist.
@alexanderpotapov27743 ай бұрын
I'm curious how do you move code to just created function? :)
@sarfrazqasim3 ай бұрын
Does action class also good for utility functions? which can be accessible in every controller of the app? Still learning Laravel so please dont get mad on my noob question.
@homamal2893 ай бұрын
Hi jeffrey whats your thought about repo and sercives pattern im using uaualy Actions + Services in my code and Thanks alot Mr best teacher in the world
@mediopalodev3 ай бұрын
100% agreed!
@brunoggdev63053 ай бұрын
Hey, where can I get that cool wallpaper?
@PaphosLife3 ай бұрын
Most websites are massively over-engineered.
@IbrahimIsmail983 ай бұрын
Laravel beyond CRUD
@ajyankee933 ай бұрын
I was watching the beginning of this video and thinking... "This seems like the 'right' way to do this with all the listeners, but in practice it can be so confusing and hard to follow" and then you said basically the same thing. This is how I would do it. And to be honest, I might even just leave it in the controller occasionally, depending on the size of the project and how many side effects of the action.
@mmelimahlobo76562 ай бұрын
Hie Jefrey what advice can you give to someone who wants to get a Job as a junior PHP developer I mean there is too much to learn when starting out.😢
@lassestube3 ай бұрын
While i agree about the code organization etc. primary actions shouldn’t be put in listeners. But as soon as you have side effects for processing on the queue, or using cqrs you do need to use some sort of queuable events. So this is still within the intermediate trap in my opinion, since all you have done is extract from the controller into an action. No db transaction used, no knowing about failure etc. I was hoping for a more advanced level solution to see how do you make that step up…. I think this needs follow up with more gradual complexity.
@kingstalker3 ай бұрын
ty nice vid , how did you do that 15:16 - 15:20
@Laracastsofficial2 ай бұрын
On VS code, it's Command + Shift + L On PhpStorm Ctrl+Alt+Shift+J ( www.jetbrains.com/guide/java/tips/find-next-word/ )
@kingstalker2 ай бұрын
@@Laracastsofficial ty
@pranithNP3 ай бұрын
What is this theme and font name that you are using?
@mihaes71723 ай бұрын
For some time now, I noticed you `whistle in the church` of so called best practices that choke the solving problem process. I'm in php since v4, I agree with you & I would like to see a more practical Laravel than 'smart'.
@mayoryorma50483 ай бұрын
That’s the way, I would just add database transaction.
@jeffreyway5203 ай бұрын
Yep.
@Aalii63 ай бұрын
👍👍
@joshhh46423 ай бұрын
To simplify, dont over architect things / complicate things. Just make your code more intuitive/straightforward as much as possible.
@obadaalzidi64523 ай бұрын
Nice refactor, but in my opinion we are not concidering reusability here ! I'm pretty sure that there are alot of other places where we need to record some kind of activity or increasing the user's experience by different values, So why not exracting those to their seperate actions also ?
@lukmannakib74973 ай бұрын
I need that wallpaper
@SergeiPakhomov03 ай бұрын
Why not to make one listener with all these private functions?
@prepped85513 ай бұрын
In defo a controller stuffer….
@expatExperience2 ай бұрын
Total violation of SRP and other patterns regarding clean code ! I don't like it, thanks for breaking the rules tho :)
@TeHzoAr3 ай бұрын
Nobody is doing that
@facephonesy3 ай бұрын
You guys always over engineer things, you are thinking like when someone comes back to the code he will be a complete idiot. And the solution you propose is to over complicate things so when this idiot comes he has to check 10 more files to figure out what the code does. I feel the weird urge to be over organized doesn't make any sense. But thank you for the video
@jeffreyway5203 ай бұрын
Did you watch the full video??
@facephonesy3 ай бұрын
@@jeffreyway520 no i didn't. When he started to sayvwe have to create a new file for each one of them. I stoped the video.
@Laracastsofficial3 ай бұрын
Yes, but this is the exact issue Jeffrey is discussing in the video, he is doing that to show the issue in the first place, you need to watch full video :)
@facephonesy3 ай бұрын
@@Laracastsofficial lol, I judged really fast. Apologies. He is saying exactly what I am saying