DEV Community

Discussion on: How to implement Clean Architecture with Laravel

Collapse
waynethorley profile image
Wayne Thorley

🤔 Theoretically it sounds great. But for something "clean" it's hugely verbose. Real-world I can see it adding unnecessary time and complexity to a project, compared to something like single action classes - which are still testable and keeps business logic out of the controllers.

Collapse
bdelespierre profile image
Benjamin Delespierre Author

Hey @waynethorley thanks for your comment 👍

The complexity is the price to pay for layer isolation. It is tempting to simply relocate business logic from controller to actions (that's what JetStream is doing).

But without proper isolation one of these two scenarios will inevitably happen:

Scenario 1 : the presentation logic will leak to the controller

class CreateUserController
{
    public function __invoke(CreateUserRequest $request)
    {
        try {
            $user = app(CreateUserAction::class)->handle($request->validate());
        } catch (UserAlreadyExists $e) {
            return redirect()->back()->with('error', "User already exists!");
        } catch (Exception $e) {
            return redirect()->back()->with('error', "Unexpected error");
        }

        if ($user->wasRecentlyCreated) {
            return view('user.show', compact('user'));
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

It might be fine for simple presentation logic but as your project grows, more logic will "leak" this way to the controller, defeating the purpose of isolating things in an action.

Scenario 2 : the response logic will leak into the use-case

class CreateUserController
{
    public function __invoke(CreateUserRequest $request)
    {
        return app(CreateUserAction::class)->handle($request);
    }
}
Enter fullscreen mode Exit fullscreen mode

In that case, the action will have to take care of choosing the view response itself, making it effectively a controller. Again, defeating the purpose...

So you could just add a presenter between the controller and the action like so:

class CreateUserController
{
    public function __invoke(CreateUserRequest $request)
    {
        return app(CreateUserPresenter::class)->present(
            app(CreateUser::class)->handle($request);
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

And that's pretty much the same thing as the implementation I proposed, structured differently and without the interfaces, but very much the same concept (also I didn't bother mentionning the request/response/view model but you get the gist at that point.)

I encourage you to play a bit with these concepts and find the right balance for your projets. It is true that CA is complex. It is intended for large scale enterprise application software and require experts on your team to operate effectively and deliver the promised value.

It is not fitted for every project. It is not a silver bullet. Nothing is.