Courses

How to Structure Laravel 9 Projects

Saving Data: Service or Action Class?

Previous: Transform Data Before Saving: Mutator or Observer
avatar

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

👍 10
avatar

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

avatar
You can use Markdown
avatar

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

avatar

Adding both to the to-do list.

avatar

Comments notifications and Code block - great! Thank you.

avatar

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

avatar
You can use Markdown
avatar

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())"

avatar

Yes, for sure!

avatar

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!

avatar

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.

avatar

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

avatar
You can use Markdown
avatar

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.

avatar

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.

avatar

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?

avatar

Yes, you can instantiate if you want.

avatar
You can use Markdown
avatar

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

avatar

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

avatar
You can use Markdown
avatar

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.)

avatar

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 :)

avatar
You can use Markdown
avatar

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.

avatar

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.

avatar
You can use Markdown
avatar

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

avatar

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.

avatar

Got it, thanks

avatar
You can use Markdown
avatar

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());
}
avatar

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

avatar
You can use Markdown
avatar

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

avatar

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

avatar

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

avatar

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.

avatar

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.

avatar

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?

avatar
You can use Markdown
avatar

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.

👍 2
avatar
You can use Markdown
avatar

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.

avatar

The service can be used in other places.

avatar

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.

avatar

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!

avatar

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.

avatar

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.

avatar

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.

avatar
You can use Markdown
avatar

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);
			}

	}
avatar
You can use Markdown
avatar
You can use Markdown