@oop_ru

Страница 529 из 785
Dmitry
24.02.2018
08:09:46
Такс по пунктам: 1. Никто нигде ничего не обязан. Делайте как вам удобно, но не вводите людей в заблуждение что это SOLID, DDD или что-то еще. 2. "мне нельзя в AR добавить ООП" ну тут я даже не знаю как комментировать... 3. "что так никак нельзя делать в AR или чём то ещё и можно лишь в их православном DM" внезапно PI никоим образом не говорит использовать DM или что-то еще, будь то AR, R(TD)G и иже с ними. Просто ваш репозиторий не несет никакой пользы. Он ничего не инкапсулирует "от слова совсем" (с) Максим. PI поверх AR пилится вполне себе легко и доступно. Собсно сам так жил с Propel лет так 5 назад. Достаточно: a) Выделить интерфейс репозитория и положить его рядом с доменной моделью. А уже в Yii будет лежать имплементация аля ActiveRecordUserRepository. б) Разделить доменную модель и AR модель и гидрировать доменную из AR (и наоборот) в том же репозитории. Для примера ваш UserRepository::findByEmail() будет выглядеть как-то так: ` function findByEmail(string $email): Shop\User { // В AR классах оставляем всю yii муть $userRecord = UserRecord::getByEmail($email); $user = new Shop\User($userRecord->name, $userRecord->email, ...); $this->uow->registerClean($user); // Если мы не хотим вызывать явно UserRepository::save(); return $user; } 3. "И готовы бить морду за то что "нет контекстов, значит код говно": дело не в том что их нет, дело в том когда они должны появится. И вроде вы на это уже ответили. Зачем возвращаться? 4. "Ну и не читают диагональщики ..." кто и что читает личное дело каждого. Поверьте ничего нового вы мне не открыли. ЗЫ. В статье годно вполне расписано все.
1. Не вводите всех в заблуждение, что я ввожу всех в заблуждение. Здесь именно остальные участники случайно нашли мой код, сказали что это DDD, потом целые сутки полоскали друг другу и мне мозг, что это "неправильный DDD" и там "всё говно". Я, наоборот, всех из заблуждения вывожу, говоря что "это не их DDD, а просто проект на фреймворке с сервисным слоем". 2. См. пункт 3. 3. Этот класс инкапсулирует "грязные" вызовы поиска и сохранения сущности, выкидывания исключений и эмита событий за "чистым" интерфейсом, вынося эти манипуляции из сервисного слоя. Это не от слова "совсем". С таким же успехом могу сказать, что ваш DoctrineUserRepository тоже не приносит никакой пользы, так как только дёргает $this->em. Повторюсь: если не нравится именно название UserRepository, то назовите его как-нибудь как UserFinderAndSaveCaller. А то, что ДОМЕННЫЕ СУЩНОСТИ ОБЯЗАТЕЛЬНО должны быть ЧИСТЫМИ и что НЕЛЬЗЯ эти же ДОМЕННЫЕ СУЩНОСТИ делать прямо на базе ActiveRecord или Propel, поместив в них те же доменные методы и прописав в них тот же маппинг тех же VO внутри в их afterFind и beforeSave - это как раз религиозный DDD-маразм, возникающий от чтения всего по диагонали. Еще забыли добавить пункты в) сделать прокси для связей и коллекций и г) спрограммировать обход этого хозяйства для отслеживания изменений.

Anton
24.02.2018
08:27:31
Ничего он не инкапсулирует, как можно раньше было вызвать напрямую User::save() в обход него так и сейчас можно. Смысл от этого UserFinderAndSaveCaller?

По поводу сущностей и нельзя, оно то можно только смысл? Как минимум у класса уже нарушен SRP. А при наличии большего количества логики поддерживать это сомнительное удовольствие.

Dmitry
24.02.2018
08:47:58
Ничего он не инкапсулирует, как можно раньше было вызвать напрямую User::save() в обход него так и сейчас можно. Смысл от этого UserFinderAndSaveCaller?
Тогда ваш DM тоже ничего не инкапсулирует. Как можно было делать напрямую $pdo->execute('UPDATE ...') в обход него, так и сейчас можно.

Google
Anton
24.02.2018
09:01:20
Почему мой? У меня его нет например.

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

Maxim
24.02.2018
09:11:25
И вы утрируете, а не дискутируете. Технические ограничения всегда будут, я говорю о том что пользы от конкретного класса у вас нет и только.
function findByEmail(string $email): Shop\User { // В AR классах оставляем всю yii муть $userRecord = UserRecord::getByEmail($email); $user = new Shop\User($userRecord->name, $userRecord->email, ...); $this->uow->registerClean($user); // Если мы не хотим вызывать явно UserRepository::save(); return $user; } Я так понимаю здесь создается пользователь, если он не нашелся в бд по email? В этом суть репозитория?

Anton
24.02.2018
09:12:15
Он и не создает

Sergey
24.02.2018
09:12:16
и это не всегда возможно

Maxim
24.02.2018
09:12:24
так в чем суть репозитория?

Sergey
24.02.2018
09:12:47
так в чем суть репозитория?
репозиторий это абстракция которая должна представить для нас базу данных как коллекцию сущностей

и все

Anton
24.02.2018
09:12:50
Чтобы инкапсулирувовать логику получения пользователя из хранилища

Google
Maxim
24.02.2018
09:13:12
Он и не создает
$user = new Shop\User($userRecord->name, $userRecord->email, ...); $this->uow->registerClean($user); А это что происходит?

Anton
24.02.2018
09:14:06
$user = new Shop\User($userRecord->name, $userRecord->email, ...); $this->uow->registerClean($user); А это что происходит?
Только говорим нашей uow что у нас есть чистый объект только что поднятый из базы

Sergey
24.02.2018
09:14:08
Это в идеальном мире
не ну понятн что чистые модели нам могут только сниться, но идея то простая

Maxim
24.02.2018
09:14:28
какая-то чернь
почему вы не оспариваете этот пример от @zloyuser ? это же чернь в чате про нормальное ООП

Sergey
24.02.2018
09:14:58
@zloyuser это ты типа заворачиваешь данные в модельку и менеджет ее делаешь?

Sergey
24.02.2018
09:16:05
а

Anton
24.02.2018
09:16:36
Доменная модель отдельно, модель данных отдельно

Sergey
24.02.2018
09:16:46
если я правильно понял @zloyuser

меня просто нэйминг смущает)

Anton
24.02.2018
09:17:02
Все верно

Sergey
24.02.2018
09:17:45
дальнейший шаг будет гидрация через рефлексии, что бы сущность юзера понятия не имела о том что с момента ее инициализации в первый раз ее многократно загружают из базы

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

Anton
24.02.2018
09:18:42
Как вариант. Ну а дальше уже разделение моделей чтения-записи.

AR вообще то отличный паттерн, если его приготовить

Maksim
24.02.2018
09:19:33
приготовить на выброс)

Google
Anton
24.02.2018
09:20:46
Ну года так три-четыре назад я бы тоже сказал. Сейчас вижу много кейсов использования.

Sergey
24.02.2018
09:21:11
AR как по мне вообще отлично работает на чтение... прям замечательно.

Maksim
24.02.2018
09:21:21
при 1 большом условии

Sergey
24.02.2018
09:21:23
просто, быстро, удобно

Anton
24.02.2018
09:21:45
кинуть исключение, вернуть null

Sergey
24.02.2018
09:21:50
А если нет пользователя с таким email, как мой код должен выглядеть?
ну по идее либо метод должен вернуть null либо кинуть исключение - в зависимости от того какое поведение требуется

Maxim
24.02.2018
09:22:21
кинуть исключение, вернуть null
В каком месте я здесь это должен сделать? В каком из этих методов?

Anton
24.02.2018
09:22:23
мне привычно когда find* возвращает или инстанс или null, тогда как get* или инстанс или кидает исключение

Anton
24.02.2018
09:23:14
function findByEmail(string $email): ?Shop\User { // В AR классах оставляем всю yii муть if (!$userRecord = UserRecord::findByEmail($email)) { return null } $user = new Shop\User($userRecord->name, $userRecord->email, ...); $this->uow->registerClean($user); // Если мы не хотим вызывать явно UserRepository::save(); return $user; }

Sergey
24.02.2018
09:23:54
осталось еще быстрый рецепт как быстро написать UoW + IM

Sergey
24.02.2018
09:24:55
я как-то накидывал Макарову на тему "а почему никто не делает AR с инвертированной иерархией наследования", ну мол с прокси классами и т.д.

можно было бы сделать прикольно

Anton
24.02.2018
09:25:13
Хороший UoW это не быстро в PHP. Много нюансов с клонами, сериализацией и прочее. Монстр доктрины тому подтверждение.

Sergey
24.02.2018
09:25:41
Хороший UoW это не быстро в PHP. Много нюансов с клонами, сериализацией и прочее. Монстр доктрины тому подтверждение.
ну если тебе не нужен универсальный UoW то не особо то и сложно. ДОктрина монстр потому что хотят все проблемы решить и причем универсально

Ммм, не понял насчет инвертирования
ну тип у тебя сча User <- ActiveRecord, а можно сделать (UserRecord) <- User

где UserRecord это какой-то сгенерированный прокси класс

Google
Sergey
24.02.2018
09:26:59
у фаулера так и написано - что мол "универсальный UoW это круто конечно но оч сложно и часто не имеет практической пользы")

Anton
24.02.2018
09:27:19
т.е. AR класс будет наследовать (имплементить) доменный?

Sergey
24.02.2018
09:27:34
блин я уже не помню, год назад было

но да, идея была в том что бы мувнуть персистентность на уровень выше

что бы можно было подменять удобнее

Admin
ERROR: S client not available

Anton
24.02.2018
09:28:08
Интересная мысль, но надо переварить

Anton
24.02.2018
09:28:39
Такие штуки надо под пиво обсуждать... Хотели же собраться, и что-то идея заглохла

Anton
24.02.2018
09:29:11
https://martinfowler.com/eaaCatalog/unitOfWork.html

Sergey
24.02.2018
09:30:43
ну да, так проще

Anton
24.02.2018
09:30:45
Это нужно чтобы не вызывать вручную User::save(). В конце запроса (команды, бизнес транзакции, etc.) UoW проверяет какие объекты изменились и сохраняет изменения в базу.

Это в двух словах

Не всегда это нужно и не всегда это удобно + как уже говорилось выше достаточно сложно в имплементации с нуля.

P.S. Если ты не Сергей Протько конечно :))

Maxim
24.02.2018
09:32:30
Anton
24.02.2018
09:32:44
Doctrine ORM = DBAL + IdentityMap + DataMapper + UnitOfWork

Dmitry
24.02.2018
09:45:30
И вы утрируете, а не дискутируете. Технические ограничения всегда будут, я говорю о том что пользы от конкретного класса у вас нет и только.
Что значит "нет пользы"? Инкапсулирует всю конкретную лапшу: if (!$user = User::findOne(['email' => $email])) { throw new NotFoundException(...); } ... if (!$user->save()) { throw new RuntimeException(...); } $this->dispatcher->dispatch($user->releaseEvents(); в абстрактные две строчки: $user = $this->users->getByEmail($email); ... $this->users->save($user); экономя код и давая возможность легко перейти на DM. Суть претензии в чём? Что в вашей вселенной абстракция и инкапсуляция по-другому работает?

Google
Anton
24.02.2018
09:59:24
Претензия только одна: я пришел в ваш код и вызвал User::save() напрямую и пропали наши доменные ивенты. Ваш "репозиторий" (вообще то это скорее фасад если его задача только скрыть внутренние вызовы) никак не защищает от этого. Вариант с отдельной моделью решает такой кейс.

Anton
24.02.2018
10:02:32
Именно

Нет я понимаю что это можно решать соглашениями и прочее, но зачем если это не сложно явно выразить в коде?

Maxim
24.02.2018
10:09:44
function findByEmail(string $email): ?Shop\User { // В AR классах оставляем всю yii муть if (!$userRecord = UserRecord::findByEmail($email)) { return null } $user = new Shop\User($userRecord->name, $userRecord->email, ...); $this->uow->registerClean($user); // Если мы не хотим вызывать явно UserRepository::save(); return $user; }
|if (!$userRecord = UserRecord::findByEmail($email)) { |return null |} Беру твой код и вызываю в другом экшене if ($user = UserRecord::findByEmail($email)){ $user->email = $newEmail; $user->save() } Где твои доменные ивенты?

Anton
24.02.2018
10:11:07
Вот я прям предвидел такой наброс. Так и изменения агрегата не было с чего вы взяли что должы быть ивенты?

Anton
24.02.2018
10:12:42
По сути да, как выше Дмитрий и сказал при желании можно обойти любой код. Прямой запрос в базу, Reflection, Runkit да что угодно...

Я дописал. Юзер email поменял
Еще раз ты не изменил агрегат. Ты изменил запись в базе напрямую. Ивентов нет и это правильно

Maxim
24.02.2018
10:14:42
Тогда я не пойму притензий к Дмитрию, что в любом месте можно вызвать $some->save() Претензия только одна: я пришел в ваш код и вызвал User::save() напрямую и пропали наши доменные ивенты

Anton
24.02.2018
10:16:21
В моем варианте ответственности отделены: доменная модель умеющая защиту инвариантов, генерацию событий и иже с ним проста и понятна. И для ее получения (и сохранения если нам лень в UoW) есть репозиторий. Работа с данными вынесена в отдельный объект про который конечный юзер (наш джун) не знает.

Sergey
24.02.2018
10:16:30
я пришел в ваш код и вызвал User::save() напрямую и пропали наши доменные ивенты А зачем ты так сделал? Как индус поступил)))
мне это напомнило историю одного знакомого джависта в свою быстность джуном: Менеджер: Мне тут сказали, что вы пишите как джуны Разработчики: ЗДрасте, а мы кто по вашему такие? Менеджер: мм... ну.... не пишите как джуны...

Anton
24.02.2018
10:17:47
Более того класс UserRecord может вообще не существовать в дев окружении, а генерироваться только при сборке. И вызвать его напрямую не получится.

Sergey
24.02.2018
10:17:53
Тогда я не пойму притензий к Дмитрию, что в любом месте можно вызвать $some->save() Претензия только одна: я пришел в ваш код и вызвал User::save() напрямую и пропали наши доменные ивенты
код поддерживают разные люди, код демонстрируемый Дмитрием хоть и имеет смысл - этот смысл надо объяснять. Ну то есть если инструментом надо учиться пользоваться и он разрешает пользоваться не правильно - это плохой инструмент

при том что всю ту де инкапсуляцию/абстракцию можно было бы сделать не сильно усложняя код и не вводя дополнительные програмные сущности

Страница 529 из 785