Может войдёшь?
Черновики Написать статью Профиль

Соблюдение принципов SOLID при работе с фреймворком Laravel

SOLID

SOLID — принципы программирования, следуя которым можно добиться легко-масштабируемого и легко-поддерживаемого кода.

Controller Service Repository

Controller Service Repository — архитектурный паттерн, необходимый для разделения ответственности и помогающий соблюдать принципы SOLID в фреймворке Laravel

Контроллеры

Контроллеры — классы, отвечающие за обработку запросов. Таким образом ответственность контроллера — это формирование ответа на пользовательские запросы.

Метод контроллера должен:

1. Принять запрос (request)
2. Запустить метод сервиса
3. Обработать исключения или возвращенное сервисом значение
4. Ответить (response) в нужном формате

Пример:

PHP
<?php

namespace App\Http\Controllers;

use 
App\Exceptions\UserNotFoundException;
use 
App\Http\Controllers\Controller;
use 
App\Http\Requests\ShowUserRequest;
use 
App\Services\UserService;
use 
Illuminate\View\View;

final class 
UserController extends Controller
{
    public function 
show(ShowUserRequest $requestUserService $service): View
    
{
        try {
            
$user $service->getUser($request->getUserId());
        } catch (
UserNotFoundException $exception) {
            return 
view('user.not_found', ['user_id' => $request->getUserId()]);
        }

        return 
view('user.profile', ['user' => $user]);
    }
}

Laravel дает возможность конвертировать исключения в Http ответы, добавив метод render, но это нарушает принципы SOLID, как минимум S — принцип единственной ответственности и не рекомендуется к применению.

PHP
<?php

namespace App\Exceptions;

use 
Illuminate\Http\Request;
use 
Illuminate\View\View;

final class 
UserNotFoundException extends Exception
{
    public function 
report(): ?bool
    
{
        
//
    
}

    public function 
render(Request $request): View
    
{
        return 
view('user.not_found', ['user_id' => $request->getUserId()]);
    }
}

Исключение не должно определять какой контент получит пользователь. Негативные последствия такой реализации в том, что при необходимости получения разных ответов в разных методах контроллеров, одно и то же исключение может быть преобразовано по разному.
В таких случаях:
1. Метод render будет содержать различные условия, проверяющие из какого именно контроллера было выброшено исключение
2. Будет не очевидно, какие ответы какой контроллер возвращает в той или иной ситуации

То же самое справедливо и для преобразования исключений в методе Handler::register()

Сервисы

Сервисы — классы, отвечающие за бизнес логику. В Laravel по умолчанию не создана директория сервисов. Вы должны создать её самостоятельно.

PHP
<?php

namespace App\Services;

final class 
UserService
{
    public function 
__construct(public readonly UserRepository $userRepository)
    {
    }

    public function 
getUser(int $userId): User
    
{
        
//Выполняем различные бизнес проверки
        
...
        
$user $this->userRepository->find($userId);

        if (
$user === null) {
            throw new 
UserNotFoundException("User {$userId} not found.');
        }

        return 
$user;
    }
}

В теле метода сервиса не выполняются запросы к базе данных. Только бизнесовая логика.

Репозитории

Репозитории — классы, отвечающие за сохранение и извлечение некоторого набора данных. В репозиториях нет сложной и тем более бизнесовой логики. В методах репозитория должны лишь формироваться и выполняться запросы. В Laravel по умолчанию не создана директория репозиториев. Вы должны создать её самостоятельно.

PHP
<?php

namespace App\Repositories;

final class 
UserRepository
{
    public function 
find(int $userId): ?User
    
{
        return 
User::find($userId);
    }
}

В идеале репозиторий не должен возвращать или принимать объекты класса Model. Лучше создать собственные entity или dto.

Такой код может показаться излишне сложным. Но с опытом вы поймете, что разделение ответственности, делает работу с таким кодом и его поддержку значительно легче.

Как вы считаете, полезен ли этот материал? Да Нет

Написать комментарий

Разметка: ? ?

Авторизуйся, чтобы прокомментировать.