Комментарии 12
Сервис — это координатор, а не исполнитель
,
Модели — не свалка для методов
А где ж тогда код большой бизнес-логики хранить, если не в моделях и не в сервисах?
Понятно, что генерация эксель-отчетов может быть отдельным сервисом, но если требуется куча статистики по разным алгоритмам, куда это пихать, если не в длинный StatisticService? В котором будет 5-6 300-строчных методов на каждый алгоритм статистики.
А что будет делать StatisticService? Если он и собирает данные, и преобразовывает их как-либо, вычисляет что-то, кладет в базу, то конечно это божественный класс и так лучше не делать и делегировать задачи другим классам. Конечно не стоит плодить сущности ради сущностей, любым принципом можно пренебречь. В целом хочу напомнить, что это лишь рекомендации, а не правила, вы можете писать так, как вам и вашей команде удобно)
вероятность смены базы данных
Эту теоретическую ахинею я слышу уже наверно лет 15 - что кто-то зачем-то будет в легаси-проекте менять базу данных. После пары-тройки лет вы уже и фреймворк просто так не обновите (масса зависимостей и клиентский код под конкретную версию фреймворка), о какой смены базы речь?
Контроллер должен
Опять сухая теория (как и статья), что-то вроде размышления советских людей о коммунизме.
На практике иногда легче наговнокодить в контроллере и забыть об этом участке кода на несколько лет, чем писать 5 слоев абстракции и сервисы с одним методом, которые больше никто и никогда использовать не будет. Недавно код вычищал, как раз умники, начитавшись умных книжек, породили 100500 классов только потому, что так им сказали в этих умных книжках. В итоге все свелось к одной строчке кода.
Основной принцип, который должен быть в архитектуре ПО - KISS, что бы и ты спустя время разобрался и "человек с улицы". Все остальное нужно добавлять по мере необходимости. Если ваш контроллер использует
User::where('is_active', true)->get()
то ничего смертельного нет, если вы не обернете эту строку в череду классов и абстракций. А вот проект, который на основе умных книжек разрастается до уровня монстра - это страшно.
Как и написано в конце, ко всему нужно подходить с умом) Конечно, иногда можно забыть о всех правилах, иногда нельзя, решать вам, я лишь показал, как можно делать и как это делать рекомендуют.
Несколько раз встречал смену БД. И с ms SQL на postgres из-за импортозамещения. И с всякой экзотики потому что это экзотика.
Но слой совместимости создавался после того как начинался переход. Заранее такой фигнёй никто не занимался. И это правильно.
На практике иногда легче наговнокодить в контроллере и забыть об этом участке кода на несколько лет, чем писать 5 слоев абстракции и сервисы с одним методом, которые больше никто и никогда использовать не будет
Иногда да, иногда нет)
Основной принцип, который должен быть в архитектуре ПО - KISS, что бы и ты спустя время разобрался и "человек с улицы".
Как только узнаю на собеседовании, что в компании подход к проектированию архитектуры по принципу типа "чтобы разобрался вчерашний студент с улицы", то сразу понимаю, что время на такую компанию тратить не хочу.
Потому что понятно, откуда ноги растут, какая там текучка и т.п.
Отличный индикатор, который экономит время соискателей.
Типичный случай смены базы - моки для запуска тестов. Просто иногда без тестов банально страшно выкатывать продукт. А если ваш контроллер вызывает User::where('is_active', true)->get(), то такой код протестировать трудно.
На практике иногда легче наговнокодить в контроллере и забыть об этом участке кода на несколько лет
Ну да. Потом искать такие участки столько же)
Статья про контейнеры/слои. Если стоит задача показывать юзеров ещё и с номером телефона? Полезешь в "наговнокоженное" место, которое написано в 10+ местах, чтобыUser::where('is_active', true)→get()
поменять наUser::where('is_active', true)→whereNotNull('phone')→get()
Тяжело же просто сделать →onlyWithPhone()
Учебники для этого и пишут, чтобы кодить правильно, а не одни и те же action's по проекту размазывать.
Ну, зато тебя человек с улицы лучше понимает)
Немного духоты вам в комментарии :)
Чего не final
'ите OrderDTO
? От него же никто по идее не должен наследоваться. Так же, как мне кажется, код был бы чище если бы использовалось https://www.php.net/manual/en/language.oop5.decon.php#language.oop5.decon.constructor.promotion
Repository - это класс, куда мы выносим какую-либо логику работы с базой данных. Чаще он используется для приложений, где есть вероятность смены базы данных.
В первую очередь это даёт нам low coupling - то, к чему мы все всегда стремимся, а уже low coupling даёт возможность сменить БД без танцев с бубном.
И я правильно понимаю что FilterDataForInsertAction это интерпретация https://refactoring.guru/ru/design-patterns/command ??
Спасибо за дополнение! Пока не выработал привычку финалить классы, обязательно исправлюсь, но да, от него не должны наследоваться. Action`ы, по крайней мере в Laravel (пример контроллера, пример экшена) обычно не используют ещё одну абстрактную прослойку, как указано в статье, что вы приложили, но суть та же.
Итого выходит почти DDD. В целом предложение очень хорошее. Смена БД, как тут хейтили, встречается часто, но смысл именно в SRP в первую очередь. Сразу добавлять эти классы легко, это 5-10 процентов на задачу. А самое важное в результате - это легко читаемый проект, легко расширяемый и легко поддерживаемый.
Архитектура в Laravel. Как сделать код понятным и масштабируемым