Если открыть книжки или сходить на любую айтишную конференцию, то SOLID тебе будут продавать как Библию. В реальности это не свод святых заповедей, а набор «не будь дебилом, дели ответственность и не плодите монстров из классов».
Laravel вроде как сам подталкивает к этим принципам, но и даёт простор для того, чтобы всё просрать. Тут уж зависит от того, пишешь ли ты проект на пару недель или систему, которая должна жить несколько лет, переживая по пути стажёров, которые будут ломать то, что ты строил.
SRP — единственная ответственность
Фраза из книжки: «каждый класс должен делать только одно».
На практике в Laravel это значит:
- Модель не должна превращаться в помойку бизнес-логики. Пусть достаёт данные и молчит.
- Контроллер — не место для твоих бизнес-правил. Там только приём запроса и ответ.
- Сервисы — туда сваливай всю грязь. Хочешь генерировать отчёты? Хочешь городить логику скидок? Пихай сюда.
Видел проекты, где в контроллере по 1000 строк кода. Выглядит как туалет в общаге после вечеринки — зайти можно, но лучше не надо.
OCP — открыто для расширения, закрыто для правок
Красиво звучит. По факту — делай интерфейсы.
- Хочешь заменить
UserServiceнаAdminUserService? Если у тебя интерфейс — то дело пяти минут. - Ларавелевские ивенты и слушатели — тоже про это. Хочешь навесить «отправку SMS» при создании пользователя — не надо ковырять сервис, просто подпишись на событие.
Проблема? Разработчики ленятся делать интерфейсы (да, и я тоже), а потом удивляются, почему одно изменение ломает полпроекта.
LSP — подстановка Лисков
Тут всё просто: не пиши такие классы-наследники, которые ломают поведение базовых.
В Laravel это видно, когда начинаешь расширять встроенные штуки. Например, пишешь кастомный UserRepository. Если твоя реализация не умеет отдавать юзера так же, как стандартная модель — ты облажался.
ISP — не заставляй жрать то, что не нужно
Тут про интерфейсы, которые не должны быть свалкой.
В Laravel это значит:
- Делай отдельные контракты под конкретные задачи. Не пихай «чтобы всё умел».
- Провайдеры должны регистрировать только то, что реально нужно.
Иначе получаешь толстый интерфейс, половину методов которого ты всё равно не реализуешь.
DIP — зависимость от абстракций
Красиво звучит, но по сути — не зашивайся на конкретные реализации.
- В Laravel контейнер умеет сам подсовывать зависимости по интерфейсам.
- Ты пишешь
UserServiceInterface, а уже контейнер решает, какой именно класс подставить.
Профит? Можешь заменить реализацию, не трогая контроллеры.
Пример
interface UserServiceInterface
{
public function createUser(array $data): User;
}
class UserService implements UserServiceInterface
{
public function createUser(array $data): User
{
return User::create($data);
}
}
class UserController extends Controller
{
private $users;
public function __construct(UserServiceInterface $users)
{
$this->users = $users;
}
public function store(Request $r)
{
return $this->users->createUser($r->all());
}
}
Красота в том, что завтра ты можешь написать ApiUserService, который будет дёргать внешний сервис вместо локальной базы. Контроллеру пофиг.
Тут подключается Laravel контейнер. В AppServiceProvider (или отдельном провайдере) прописываешь, какая реализация подсовывается:
public function register()
{
// для продакшна работаем через API
$this->app->bind(UserServiceInterface::class, ApiUserService::class);
// а можно и так: в dev — локальная база, в prod — API
// if (app()->environment('local')) {
// $this->app->bind(UserServiceInterface::class, UserService::class);
// } else {
// $this->app->bind(UserServiceInterface::class, ApiUserService::class);
// }
}События: пример «по-ларевелевски»
class UserRegistered
{
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
}
class SendWelcomeEmail
{
public function handle(UserRegistered $event)
{
Mail::to($event->user)->send(new WelcomeMail());
}
}
$user = User::create($request->all());
event(new UserRegistered($user));
Хочешь ещё навесить уведомление в Telegram? Просто добавь ещё слушатель. Никто не полезет ломать UserService.
Итог
SOLID в Laravel — это не повод строить храм архитектуры. Это про то, чтобы через полгода тебе самому не хотелось удалить проект и уйти в запой.
- Делишь ответственность — меньше кода в одном месте, проще чинить.
- Абстрагируешься — завтра можно переписать кусок без боли.
- Не лепишь монстров — команда не проклянёт твой код.
Хотя, честно, если ты пишешь CRUD для маленького стартапа, можно и без SOLID. Но если проект живёт больше года — не соблюдаешь принципы, будешь каждый день разгребать свои же кучи дерьма.
0 комментариев