Skip to main content

Saving Data: Service or Action Class?

Premium
5:53

The Full Lesson is Only for Premium Members

Want to access all of our courses? (31 h 16 min)

You also get:

55 courses
Premium tutorials
Access to repositories
Private Discord
Get Premium for $129/year or $29/month

Already a member? Login here

Comments & Discussion

T
Taboritis ✓ Link copied!

There is no difference between Action and Service in your approach. It is only contractual matter.

Better way is to make Action class invokable and calling it by $action() statement.

class CustomAction {
    public function __invoke() {
		    // do something
		}
}

public function doSomething(CustomAction $action)
{
    $action()
		
		return redirect('/somewhere');
}

PS. Improve displaying code blocks :D

V
Vadim ✓ Link copied!

+1 for the code block (it's needed, especially on this kind of site)

V
Vadim ✓ Link copied!

Would be great to have notifications about all replies to the comments. And see "My comments" list.

PK
Povilas Korop ✓ Link copied!

Adding both to the to-do list.

V
Vadim ✓ Link copied!

Comments notifications and Code block - great! Thank you.

DL
David Lun ✓ Link copied!

you can see My Comments option now in your top-right menu

V
Vadim ✓ Link copied!

Can we get access to the Service by using "use" at the top of the file, like: use App\Services\UserService;?

And then i nthe code use it as: "$user = UserService::create($request->vadated())"

PK
Povilas Korop ✓ Link copied!

Yes, for sure!

V
Vadim ✓ Link copied!

Thank you. Is there are difference or preferable way to access the Service or all 3 way (2 from lecture and 1 from my comment) are equal and same performance-wise?

I also got the notification about the reply. Thank you for that too!

V
Vadim ✓ Link copied!

BTW, you can try to add/change those parameters to your "comments-comment" CSS class:

background-color:#334155; border-radius:4px; padding:16px;

so, they will fit the overall style of the site.

PK
Povilas Korop ✓ Link copied!

Yes, all ways to access ar equal and same, it's your choice.

R
Redrum ✓ Link copied!

I don't understand, why you don't use Dependency injection on your controller but you create a new instance of UserService ? :

private UserService $userService;

public function __construct( private readonly UserService $userService ) { }

...

public function store(StoreUserRequest $request) { $user = $this->userService->create($request->validated()); }

With this, your controller does not have a strong dependency with the UserService.

Same case for UserAction.

PK
Povilas Korop ✓ Link copied!

Because maybe I need to use this Service/Action only in that one method, and I don't need it for all Controller? But if you use this class in more than one method, then yes, makes sense to define it in the Constructor.

R
Redrum ✓ Link copied!

Thank's a lot! If I understand correctly, when a class is used only once, we might as well instantiate it directly instead of going through a constructor?

PK
Povilas Korop ✓ Link copied!

Yes, you can instantiate if you want.

F
fpolli ✓ Link copied!

Do we need to register the service with a service provider?

PK
Povilas Korop ✓ Link copied!

No, Services are not the same as Service Provider. You can watch more about service providers here: https://laraveldaily.com/video/laravel-service-providers-all-you-need-to-know

P
Pēteris ✓ Link copied!

Thank you for the lecture. Fairly easy to understand. Thanks for pointing out that we MAY take code out of Controller (put in Service or Action etc.). Could you please elaborate, when we SHOULD do it (and how it would benefit us later in development)? For example, when working on a larger project? Or - does it just depend on a developers personal preference? (By taking code away from a Controller I loose an overview, and it bothers me.)

PK
Povilas Korop ✓ Link copied!

The main benefit is the separation of concerns principle. Meaning separate parts of the code are responsible only for their individual action/job/task/whatever you call it. Then it is easier to work on improving/fixing THAT individual part of the code, without touching anything unrelated in any other files.

It's just easier to understand and work on 5 lines of code, than on 50-line Controller method.

Especially if you work in a team, and the code is opened by someone else in the future. Or even yourself in the future, in 6 months you will totally forget what code you're writing today :)

YR
Yeasin Rahman Siam ✓ Link copied!

Hello povilias sir, i am currently building a website where i have admin dashboard side and client side.. how do i separate service and action classes for admin and client side .. or sometimes some service or action logics are common for both admin and client side.. mybe we can call global services or actions ... How do i structure those service and action classes.

PK
Povilas Korop ✓ Link copied!

It's totally your personal preference, and depends on those "sometimes" cases as you described, how common and similar they are. Without specific debugging of the situation, I cannot advise on how to structure it.

D
Dima ✓ Link copied!

As always a good lesson.

May I ask you, let's say I'm working on an API and some condition doesn't pass, is it better to return error json directly from Services or just throw Exceptions and catch them in Controllers?

Thanks

PK
Povilas Korop ✓ Link copied!

Service/Action class shouldn't "know" about what should be returned to the user, they are just internal classes. So if something goes wrong, you should throw exceptions.

Some exceptions are caught automatically by Laravel, or you can catch them yourself in the Controller.

D
Dima ✓ Link copied!

Got it, thanks

AA
Ali Awwad ✓ Link copied!

How about adding "static" to execute action method?


// in the action (notice the "static")
public static function execute(array $data) : User
{
	return User::create($data);
}
		
// in controller, no need for injecting
public function store(UserStoreRequest $request)
{
	CreateUserAction::execute($request->validated());
}
PK
Povilas Korop ✓ Link copied!

Yes that's another option but generally static methods are considered as "last resort" where nothing else fits well. At least in my experience.

R
ripon52 ✓ Link copied!

How can i use Interface with Service File and call it in a Controller sir?

PK
Povilas Korop ✓ Link copied!

For examples of interfaces, I guess it's better you watch my course SOLID in Laravel

R
ripon52 ✓ Link copied!

Appreciate it, Can you please share any existing example repositories of yours where u applied interface with service file?

PK
Povilas Korop ✓ Link copied!

I don't think I have any repository specifically for that example. In my cases, Service is a Service class without the interface, it's flexible for any changes.

Interfaces are for strict structure for MULTIPLE classes to implement that interface. I don't feel the need for multiple Services.

R
ripon52 ✓ Link copied!

i am doing a Ridesharing project. Can you give me any suggestion, where i should use Interfaces? Mainly, i write all the business logic in my service class. I didn't use interface before.

PK
Povilas Korop ✓ Link copied!

If you don't need to use interfaces, then DON'T use them, why do you feel the need to use interfaces at all in this case?

M
Marcel ✓ Link copied!

Nice! Small tip, instead of a 'New file' you can create a 'New PHP Class` in PHPStorm so you dont have to set the namespace and class manually.

J
jakub ✓ Link copied!

Waht architectural problem is solved by adding Services? I see none, we just move code as it is from one place to the other, we get skinny controller and bloated service that mixes concerns and responsibilities.

DS
Dmytro Sakharuk ✓ Link copied!

The service can be used in other places.

J
jakub ✓ Link copied!

I know what are services, but application should be divided into layers, ale logic of one layer should not leak into anoher - see Onion or Clean architectures. Just offloading logic to some random service is equally good as keeping it in controller or model.

M
Modestas ✓ Link copied!

You are technically right with this. Services indeed are usually 1:1 copies of lets say controller. But what this allows us to do is smart refactoring - move small action into its own method and use anywhere. Do you generate a slug? Well, move that into it's own function. Then you can use the slug generation without this exact action.

Or in other words - services are like transitional layer to something better. Moving away from services to lets say domain pattern or action pattern - is easier. This is because you will most likely refactor some of your code to be split into smaller accessible functions, so moving them - is faster.

ps. Services is just one of many ways to help you move into the right direction and are not a solution for DDD, Event driven, Action systems. It's more like a beginning step!

J
jakub ✓ Link copied!

Well, point of view depends on where you sit.

For me mixing application, domain and infrastucture layer and offloading it to additional file is the same as keeping it in one place, a controller.

I did not meant DDD when I mentioned Onion/Clean. Both acrhitectures just divide application into layers, leaving the Core/Domain a black box. They do not even mention what should be in it, and this is where DDD can be iplemented.

I do not think that movig code from one place to another helps in any way to apply any of those patterns you or I mentioned, and in fact services implemented like this very often are just a way to sweep unmaintanable, untestable code under the rug; and this was my point.

This is how my typical project build using Onion and implementing DDD is structured.

There are Application, Domain and Infrastucture Services and each of this services works in their own layer. Sometimes I also have something called "Tools" that contains for an example services that does not belong to any of the 3 layers (can be used in any of those, for an example observability service).

UserInterfaces (eg controllers) perpare the input and then execute a UseCase that belongs to Application layer. Each UseCase executes single "task" (eg CreateUser, UpdateUser) and is independent on UserInterface (can be run from RestController, HtppController, CLI or by job) and orchstrate application by interacting with it's services.

M
Modestas ✓ Link copied!

I can be honest with you - there is no one solution for this problem. What you described is true to some extent. Mainly, I would not agree with the:

this very often are just a way to sweep unmaintanable, untestable code under the rug; and this was my point.

As this is not the case. If properly done - services will be responsible for a single action and you can test them directly without any interaction on routes. In my case, services are the logic, which receives data from a controller/whatever and does an action. So one service method technically does 1 action based on the input it received. This is testable and in my experience - works without introducing a mess. So it does sound like the way you do the system, except ours is less dependant on interfaces/abstractions. Which I would argue is a better place to start and grow from there.

But then again, it does depend on the system. Some systems can do this, others should use more advanced things.

J
jakub ✓ Link copied!

So it does sound like the way you do the system

No it doesn't. If you write tests, yours are in 90% feature tests coupled with database and everything else that makes IO. I have few featue tests, and I try to avoid them because of how long they take (to execute).

Besides... since you do not have any architectural patterns implemented, then you have no strict rules to follow so you also can't write architectural tests that force developers to do things in certain way and avoid doing things in the way which they should not. This is where the mess beginins.

I can hand over my application to other developer(s) and get back to it after years. If dev(s) followed rules imposed by architectural concepts, I will be able to tell without looking at code from where in code what processes will be triggered, and where to look for that code. I can tell the same about applications that I have never seen before, but were created using those patters.

DS
Dmytro Sakharuk ✓ Link copied!

It's better for the create method of the service class to take a DTO as an argument, as it provides more predictable and understandable behavior

For the implementation to be perfect, the service class should use an interface implementation that interacts with the infrastructure.

	interface StorageInterface
	{
			public function store(DTO $data);
	}

	class UserService
	{

			protected StorageInterface $storage;

			public function __construct(StorageInterface $storage)
			{
					$this->storage = $storage;
			}

			public function store(DTO $data)
			{
					$this->storage->store($data);
			}

	}