adelf / acwa_book_ru

Книга "Архитектура сложных веб-приложений. С примерами на Laravel"
1.67k stars 225 forks source link

Почему считается, что модели eloquent имеют минимум две ответсвенности? #5

Open antonprogz opened 4 years ago

antonprogz commented 4 years ago

В главе "Внедрение зависимостей" встретил следующую фразу:

Классы Eloquent моделей содержат как минимум две ответственности: хранение состояния сущности и выборку/сохранение/удаление сущностей из базы

В связи с этим, у меня возникли следующие вопросы:

  1. Взаимодействие с базой это не часть ответственности по хранению данных? Я думаю хранение данных должно предусматривать взаимодействие с каким-либо персистентным хранилищем, иначе это не хранение.
  2. Почему тогда, например, обязанности класса Request не определить как:
    • хранение данных HTTP запроса
    • парсинг HTTP запроса

Я считаю, что иметь состояние для класса в ООП это не обязанность. Все объекты в ООП (даже т.н. stateless сервисы) имеют состояние, взять хотя бы внутренний идентификатор, зависимости, конфигурацию. Почему наличие атрибутов с данными в моделях Eloquent считаются отдельной ответственностью?

adelf commented 4 years ago

Привет, Антон. В главе про доменный слой я разовью мысль, что персистить данные - это просто необходимое зло) и для некоторых приложений логика в классах-моделях намного сложнее логики хранения их в базе данных. Это две разные логики, но элоквент соединяет их в одну и получаются фактически строчки в базе данных, которые с собой какую-то логику несут. Эти две ответственности и назвал. Но, вероятно, надо исправить немного это предложение как-нибудь.

githubjeka commented 4 years ago

Классы Eloquent моделей содержат как минимум две ответственности: хранение состояния сущности и выборку/сохранение/удаление сущностей из базы

Вопрос в том, что нарушается SRP?

Я не вижу тут беспокойств. Причина внесения изменений в Eloquent одна - может поменяться что-то в базе данных. Со связностью (cohesion) всё впорядке, все методы сконцентрированы одной цели - работа с данными из базы данных. А вот если бизнес логика начинает появляться в Eloquent - вот тогда плохо...

adelf commented 4 years ago

Евгений, я говорил ещё и о выборках из базы и методах сохранения/удаления. Все эти Soft delete, скоупы люди часто суют в классы моделей. И если скоупы ещё можно как-то отдельно держать (хотя это и не модно), то глобальные скоупы, такие как софт делит - не получится. Это немного другая логика, чем представление строки из бд.

mikield commented 3 years ago

А вот если бизнес логика начинает появляться в Eloquent - вот тогда плохо...

@githubjeka Много раз видел как используют подход "Тонкие Контроллеры -> Толстые Модели".

Скоупы получения данных где уже вшита бизнес логика, с (например) той же фильтрацией по типу пользовательского аккаунта (простой/премиум). Скоупы которые являются не продолжением Eloquent\Builder а которые вызывают get() и возвращают результат.

Так что в реальных проектах, тезис

Причина внесения изменений в Eloquent одна - может поменяться что-то в базе данных. получается не всегда правдивым.

И причиной изменения моделей становится банальное изменение бизнес логики.

Приведу пример с скоупом.

$users = User::onlyActive()->get();

И тут уже зарыта собака, ведь сегодня только активные пользователи это те у кого email_verified_at != NULL а завтра уже и у кого phone != null и profile()->exists() и т.д.

Удобно же правда, вроде KISS а вроде уже смешивание слоев, и в будущем, лично я, побоюсь добавить или изменить этот скоуп, ведь потому что PHPStorm и магия Laravel не дает мне интуитивно найти места где используется этот метод. Да через глобальный поиск можно, но скоупы же не всегда вызываются статично, однажды это из инстанца, а иногда вообще из Builder. Пример:

$user = new App\Models\User;
return $user->onlyActivated()->get();
 # Файл App\Models\User.php
 //...
public function scopeOnlyActive($builder) {
    return $builder->whereNotNull('email_verified_at');
}

public function scopeOnlyActivated($builder){
    return $builder->onlyActive();
}

пы. сы. В определённых кругах, за то что в Laravel используется Active Record и Model - архитекторы настаивают на замене его на Doctrine :) пы. сы. сы С последним примером скупов я сомневался, и попробовав я, честно говоря, удивлен что оно работает.

githubjeka commented 3 years ago

В вашем примере "собака зарыта" не в Active Record , а в том, что нет единого АPI получения активных пользователей.

mikield commented 3 years ago

... что нет единого АPI получения активных пользователей.

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

С одной стороны они то правы, любой обучающий ресурс им так диктует. Мол: тут мы "отфильтруем" данные с помощью "скоуп функций". А тут пользователь сам себе вышлет новый эмелй.

Я просто хотел своим сообщением вам показать что утверждение

Причина внесения изменений в Eloquent одна - может поменяться что-то в базе данных. получается не всегда правдивым. Не так верно как кажется.

И я согласен с вами, я бы тоже хотел изменять модель в соотвествии только с изменениями в схеме.