Комментарии 22
Нужно сделать декоратор? Проще сделать с командой, так как не нужно будет 100500 методов заглушек, если бы делали декоратор для большого сервиса и его одного метода.
Нужно выполнить очередь (или перевести какой то фунцкионал на нее) — команда как раз для этого отлично подходит.
Единственное но — это больше кода, а если проект небольшой, то плюсы которые выходят в статье — просто могут не понадобиться в небольшом проекте.
Неплохой доклад на тему CQRS, как раз с разбором нарастающих проблем.
www.youtube.com/watch?v=mvIXCgwGf9E
Декоратор хорошо, но SRP нарушен.
Без проблем.
Берём класс LogCreateUser и задаемся вопросом что он делает:
— создаёт пользователя
— логирует создание пользователя
— всё выше сказанное.
Ответ: всё выше сказанное. А значит нарушили SRP.
Второй момент: с какого испуга класс логирования должен наследоваться от класса создания? Композиция — да, но не наследование.
Мой вывод: логирование — это прекрасно. Но это отдельная операция, которая живет в другом слое кода, никак не связанном с созданием.
Простейший пример на псевдокоде:
log('User creation: start')
userService->create()
log('User creation: end')
Но получается, если логирование — это отдельная операция, то понадобится делать правки во всех местах где создается юзер.
У меня для вас две новости.
Во-первых, если у вас в приложении не одно место, где создается пользователь — что-то явно не так с вашим приложением.
Во-вторых, раз у вас несколько мест — хорошо, используйте декоратор. Но сделайте его так, как упомянул товарищ DExploN, а именно через правильный класс (который не наследуется, а реально декорирует вызов метода другого объекта).
log('User creation: start')
userService->create()
log('User creation: end')
Тогда вопросы:
1-В какой метод/класс надо обернуть эти строки?
2-Разве теперь уже этот метод/класс не нарушает SRP?
Если уж так зацикливаться на "правильности" и не использовать сквозное логирование на уровня среды испольнения а-ля AOP, то, да, только одно (c AOP — ни одного в исходниках). И ответственность этого метода/класса будет в декомпозиции и делегировании вызова чего-то типа $userServiceWithLogging->create() к $this->logger->info() и $this->userService->create(). Никакой другой логики кроме трех подобных строчек в нём быть не должно.
Откуда желание постоянно что-то оборачивать? Оставьте эти три строки в той Closure, которая отвечает за этот endpoint в роутере.
Можно даже сделать так:
class CreateUser
{
public function __construct(); // сохраняем входные данные
public function __invoke(); // создаем юзера
}
class LogCreateUser
{
public function __construct(Logger $logger, CreateUser $createUser) {
$logger->start();
$createUser();
$logger->end();
}
}
Некое подобие middleware, только не для request, а для методов сервисов.
Это не класс логирование, это класс создания пользователя с логированием.
Декораторы выглядят так:
SendEmailCreateUser(new StatCreateUser(new LogCreateUser(new CreateUser())))
Слишком маленькие классы, настолько же плохи как и слишком большие, особенно когда у вас 10000 файлов вместо 1000. Большая часть из которых состоит из 5-10 строк, мило, но рано или поздно разработчики перестанут понимать куда лезть чтобы добавить флажок при регсистрации пользователя...
Уверен, в 80%+ проектов User это просто черная неведомая дыра. И тут показан хороший способ уменьшить энтропию системы.
Да и в основном как раз существует проблема огромных классов, говнокода, лапшекода, который нужно как-то обобщать и логично разбивать на части. Обратной проблемы, когда сразу все разбили на 1000 классов, трейтов, хелперов, компонентов, а потом найти ничего не могут, потому что это сайт-визитка парикмахерской всего лишь — почти не существует.
$user = $createUser($request->all());
Эти 2 строки ничем не отличаются в плане дублирования.
Заменой статическоего метода на сервис решается проблема зависимости от конкретной реализации (D в SOLID), нерасширяемости и нетестируемости статических методов.
Принцип DRY на примере Laravel