Comments & Discussion
Would be great to have notifications about all replies to the comments. And see "My comments" list.
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())"
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.
Do we need to register the service with a service provider?
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
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.)
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 :)
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.
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
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.
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());
}
How can i use Interface with Service File and call it in a Controller sir?
For examples of interfaces, I guess it's better you watch my course SOLID in Laravel
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.
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.
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!
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.
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.
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.
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);
}
}
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.PS. Improve displaying code blocks :D
+1 for the code block (it's needed, especially on this kind of site)