laravel / blog-contest-may-mayhem

242 stars 16 forks source link

Keeping your Laravel applications DRY with single action classes #22

Open RemiCollin opened 6 years ago

RemiCollin commented 6 years ago

https://medium.com/@remi_collin/keeping-your-laravel-applications-dry-with-single-action-classes-6a950ec54d1d

melokki commented 6 years ago

Really nice article @RemiCollin . One question I have, what folder structure do you use to store these action classes?

RemiCollin commented 6 years ago

Thank you !

I usually group them by model/related group of models :

app/Actions/Users
app/Actions/Orders
...

Though in biggest projects, I tend to make an Actions subfolder by logical "module" of the application :

/app/Cart/Actions
/app/Users/Actions

Does not really matter I guess as long as it make sense with the project you're working with and you keep things consistant ;)

melokki commented 6 years ago

Great, thank you very much :)

JunaidQadirB commented 6 years ago

Interesting, my question is as your action classes are growing, so does the the register method of your AppServiceProvider. It would be nice if you could discuss that as well. And by the way I don't buy returning value on command class.

xstevenyung commented 6 years ago

@JeyKeu I personally use synchronous Job for this, which avoid this registering issue.

RemiCollin commented 6 years ago

@JeyKey : the register will grow only if you use adapters for your classes, otherwise classical DI works fine. Also I have a trick for generating adapters for all actions classes which prevent it from growing, but it could be the subject of an entire article by itself.

@JeyKeu & @stvnyung : Actions are not Jobs or CQRS commands. They are meant to return data in a generic way which will be translated into a Response . I think of them as the logical 'API' of an application. They can be a database Query as well. The important part is that they have to be Request/Response agnostic. I guess I should have insisted on this in the article :) Thanks for your feedback, really appreciated !

melokki commented 6 years ago

Also I have a trick for generating adapters for all actions classes which prevent it from growing, but it could be the subject of an entire article by itself.

Where I can read this article? :)

RemiCollin commented 6 years ago

@melokki : i'll ping you once i've written it :)

xstevenyung commented 6 years ago

@RemiCollin I get the point ! What is the benefits of creating a Action instead of a sync Job then ? I feel like both could do the same thing, are they really different or just the same in the end ?

P.S.: The idea of using Job like this mainly come from there https://github.com/lucid-architecture/laravel

monaam commented 6 years ago

@RemiCollin can sync jobs return objects/values? I think a job logically will contain bunch of actions.

RemiCollin commented 6 years ago

@stvnyung ; from what I read Action would be closer to the Feature part in lucid architecture. Jobs are basically fire and forget; as @monaam pointed out, they cannot return values; they are designed for time-consuming task which you do not need to wait the response for completing a request, like sending an email, posting some data to a 3rd party API... edit : after further reading, lucid's jobs are not really like laravel's jobs hence the confusion. I'd say that the main difference is by design is that Actions always have resolvable dependencies, and can be injected inside a controller/method, and lucid's jobs have their parameters in the constructor, so they cannot be injected

@monaam : Yes I often embed one/several actions inside a job, if I need a time consuming script to run async in the queue.

twocngdagz commented 6 years ago

@RemiCollin ping me too, great article 👍

orangeShadow commented 6 years ago

@RemiCollin and me! 👍

RemiCollin commented 6 years ago

@melokki @twocngdagz @orangeShadow : Decorating Action classes using generated proxies

ghost commented 6 years ago

@RemiCollin

Great article, I think what are you doing is somewhat ( ADR pattern ), but you don't separate the response in a new class ( the ADR way ).

In your way, how you deal with ( API Resources ) and where you put them ? inside actions ? or in the controller ?

alacayo commented 5 years ago

@RemiCollin great article. How do you deal with retrieving models. Do you create an action for a simple User::where('active', 1)->orderBy('id', 'desc')->get()?

michaelkove commented 4 years ago

I know it's an older post. @RemiCollin this is great article, before running into it, I used similar approach (but with Service providers), but the problem I ran into is dealing with nested eager loading. Maybe you can shed some light onto it:

Let's take your example: User (1-M)> Blogs (1-M)> Comments > (1-M)> User

Let's say I need to load user with latest blog posts and 5 top comments with comment's user. Do I eager load in my Action/User/LoadUser.php

User::with['blog','blog.comments', 'blog.comments.user'])...

But let's say I don't need to load comments with their users for some sections of my website. Should I create: Action/User/LoadUserWithComments.php ?

Sometimes nested eagerloading can get pretty hectic (Especially for reporting) Or do I create a separate actions for each situation? Seems to be a lot of redundant code. Especially if business logic comes at play. (For example, only certain UserType(s) can be loaded or only logged in users can see comments. etc).

Maybe I am misunderstanding this?

1212087 commented 4 years ago

Hi, @RemiCollin I have the same question with @michaelkove, should we create a new action class for a simple action such as getting data from a table? And how to deal with more complex queries that come with some different sections? Such as some controllers need A & B action, but some controllers else also need C & D (both A, B, C, D are just simple actions). Should I create AActionClass, BActionClass, CActionClass and DActionClass? This issue was not active for a long time, I glad to hear from you, sir!