Repository vs ActiveRecord

Обсуждаем, как правильно строить приложения
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Repository vs ActiveRecord

Сообщение BrusSENS »

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

Итак, начнём.
AR - крутая штука, умеет многое, удобна, просто работать со связями. Но... Слишком жирные "модели", нарушает single responsibility, от большого количества связей раздувается до неимоверных размеров. В итоге, без опыта использования может попить много крови. Например, встречал, когда в User "модели" описывали все связи, которые есть в проекте, со всеми другими "моделями", жирнее некуда было.

Репозитории - удобная весчь. Позволяет управлять коллекцией, не перекладывая ответственности на сущности. Всё красиво, понятно и код вроде как выразительнее. Но... Пока делаете простые выборки (касаемо SQL) сущностей - всё круто и просто. Но только появляется некая ситуация, когда нужно Lazy и Eager, или нужно сделать JOIN и профильтровать поля - это просто превращается в поиск элегантного решения, которое очень сложно найти. Некоторые советуют "юзать прокси", насчёт этого я вообще промолчу.

Так вот хотелось бы от вас услышать ваше мнение по поводу решения подобных проблем при разработке, может кто уже имеет элегантные решения, ведь сама идея, лично мне очень нравится, но вот всё портит сложность реализации подобных гибких выборок, связей и фильтров.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение samdark »

Модели слишком жирные только если вы сами их такими сделаете. Обычно они умеренно жирные :) SPR нарушает AR по определению. Он для этого и сделан и поэтому для многих задач удобен, а для некоторых фатально непригоден. Только связями катастрофически раздуть AR как-то не особо реально. Методы простейшие, назначение одно.

Да, репозитории писать бывает не просто и не быстро. Зато можно запилить оптимально и перепилить в одном месте, если что.

За гибкость приходится платить... за негибкость тоже :)
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение BrusSENS »

samdark писал(а): 2017.09.04, 14:25 Да, репозитории писать бывает не просто и не быстро. Зато можно запилить оптимально и перепилить в одном месте, если что.
В том то и дело, что пока всё просто - всё хорошо. Опять же ясно, что серебряной пули не бывает.
samdark писал(а): 2017.09.04, 14:25 За гибкость приходится платить... за негибкость тоже :)
Это да. Поэтому и хочется, что бы знающие люди поделились своим опытом, т.к. встречающиеся абстрактные решения ничего общего с реальностью не имеют. Спецификации? Бред, как по мне, удобства не добавляют, лучше какой-то абстрактный билдер критерии сделать. И пять таки вопрос в том, как правильнее сделать передачу JOIN фильтров по другим репозиториям. Я в соседней теме своё видение связей представлял, но ни одного ответа так и не получил.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение BrusSENS »

samdark писал(а): 2017.09.04, 14:25 Только связями катастрофически раздуть AR как-то не особо реально. Методы простейшие, назначение одно.
Тут касательно AR могу сказать, что было бы не плохо объяснять знакомящимся с AR и Yii в целом, что из себя представляют связи и для чего они нужны.
Например есть User, UserProfile и Post. User должен внутри себя только по getProfile получать связь с профилем, а посты отбирать через Post::findByUserId($userId); вне самой модели. Тут SPR тоже часто нарушают, имхо. Не должен User знать о Post'ах, ни его это прерогатива.
P.S.: немного отошёл от темы.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Repository vs ActiveRecord

Сообщение zelenin »

BrusSENS писал(а): 2017.09.04, 15:09лучше какой-то абстрактный билдер критерии сделать
или не обязательно абстрактный и не обязательно билдер. Просто критерию. Можно под конкретный репозиторий написать, постепенно усложняя при необходимости. Я так сделал на текущем проекте.
BrusSENS писал(а): 2017.09.04, 15:09И пять таки вопрос в том, как правильнее сделать передачу JOIN фильтров по другим репозиториям.
непонятна проблема.
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение BrusSENS »

zelenin писал(а): 2017.09.04, 15:38 или не обязательно абстрактный и не обязательно билдер. Просто критерию. Можно под конкретный репозиторий написать, постепенно усложняя при необходимости. Я так сделал на текущем проекте.
Если не сложно, можете пример привести? Просто в моём понимании, хорошая критерия - это билдер, неважно текучий или нет. Посему хочется увидеть, как ещё можно подойти.
zelenin писал(а): 2017.09.04, 15:38 непонятна проблема.
Например, есть у нас агрегат User, Order и Product. По логике User знать о Order и Product не должен. Но нам нужно вывести пользователей, которые фильтруются по некой таблице из агрегата Order, например, пользователей, заказавших некоторый Product. AR на позволяет юзать joinWith, вроде как, а вот что делать в случае, когда у нас получение всех сущностей происходит через репозиторий? Надеюсь понятно объяснил.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Repository vs ActiveRecord

Сообщение zelenin »

BrusSENS писал(а): 2017.09.04, 16:09 Если не сложно, можете пример привести? Просто в моём понимании, хорошая критерия - это билдер, неважно текучий или нет. Посему хочется увидеть, как ещё можно подойти.
ну билдер - это реализация заполнения атрибутов объекта. не в этом же соль.
у меня go.
что то типа такого:

Код: Выделить всё

$phoneCriteria = (new PhoneCriteria)
    ->field('session_id', $sessionId)
    ->sort('id', 'desc');
$phoneRepo->findByCriteria($phoneCriteria);
BrusSENS писал(а): 2017.09.04, 16:09Например, есть у нас агрегат User, Order и Product. По логике User знать о Order и Product не должен. Но нам нужно вывести пользователей, которые фильтруются по некой таблице из агрегата Order, например, пользователей, заказавших некоторый Product. AR на позволяет юзать joinWith, вроде как, а вот что делать в случае, когда у нас получение всех сущностей происходит через репозиторий? Надеюсь понятно объяснил.
не надо смешивать понятия агрегат и таблицы.
надо заджойнить - заджойните на мощностях вашей ORM, не надо в данном контексте упоминать другие агрегаты и репозитории. Т.е. ничего пробрасывать не надо.
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение BrusSENS »

zelenin писал(а): 2017.09.04, 16:18 ну билдер - это реализация заполнения атрибутов объекта. не в этом же соль.
у меня go.
что то типа такого:

Код: Выделить всё

$phoneCriteria = (new PhoneCriteria)
    ->field('session_id', $sessionId)
    ->sort('id', 'desc');
$phoneRepo->findByCriteria($phoneCriteria);
Ну примерно такой вариант я и реализовал:

Код: Выделить всё

$criteria = (new UserCriteria)
    ->equals('fieldId', $val)
    ->like('fieldId', $val)
zelenin писал(а): 2017.09.04, 16:18 не надо смешивать понятия агрегат и таблицы.
надо заджойнить - заджойните на мощностях вашей ORM, не надо в данном контексте упоминать другие агрегаты и репозитории. Т.е. ничего пробрасывать не надо.
Это то понятно, что таблицы у нас тут не при чём. Но всё таки нужно быть реалистами. Репозитории работающие с РСУБД в качестве хранилища будут отличаться от репозиториев для отличных от РСУБД хранилищ.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Repository vs ActiveRecord

Сообщение zelenin »

BrusSENS писал(а): 2017.09.04, 16:42Это то понятно, что таблицы у нас тут не при чём. Но всё таки нужно быть реалистами. Репозитории работающие с РСУБД в качестве хранилища будут отличаться от репозиториев для отличных от РСУБД хранилищ.
я реалист и практик, но не понимаю о чем вы. Возможно вам кажется очевидным какие-то моменты, но вы лучше их поясняйте.

У меня например есть под рукой два приложения - на postgres и на mongo. Конечно они ВНУТРИ отличаются - ведь используются разные либы для доступа, но меня мало интересует что там внутри, я ведь репозитории создавал, чтобы знать, что получу наружу.
Поясняйте.
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение BrusSENS »

И вот появился вопрос, например, нужно выбрать заказы пользователя, я принципиально считаю, что сущности заказов не должны быть вложены в сущность пользователя, поэтому какая реализация подойдёт больше?
1. Выбираем заказы, передав в качестве аргумента OrderRepository->getAllByUser() ID или всю сущность?:

Код: Выделить всё

$user = $userRepo->gelById(1);
$orders = $orderRepo->getAllByUserId($user->getId());
// Или
$users = $userRepo->getById(1);
$orders = $orderRepo->getAllByUser($user);

$view->render(
     'user' => $user,
    'orders' => $orders
]);
Склоняюсь ко 2 варианту из-за возможного различия типа ID (int/string)

2. Выбираем все заказы всех пользователей, и передаём их как VO или по отдельности:

Код: Выделить всё

$users = $userRepo->getAll();
$orders = $orderRepo->getAllByUsers(Array::arrayFromPropertyValues($users, 'id'));

$view->render(
     'user' => $user,
    'orders' => $orders
]);

// или

$users = $userRepo->getAll();
$orders = $orderRepo->getAllByUsers(Array::arrayFromPropertyValues($users, 'id'));

$collection = new UserWithOrdersCollection($users, $orders);

$view->render(
     'collection' => $collection
]);
Опять же склоняюсь ко второму варианту, т.к. по идее это у нас получается некая коллекция.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение samdark »

А чего не getUserWithOrders()?
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение BrusSENS »

zelenin писал(а): 2017.09.04, 16:48 я реалист и практик, но не понимаю о чем вы. Возможно вам кажется очевидным какие-то моменты, но вы лучше их поясняйте.

У меня например есть под рукой два приложения - на postgres и на mongo. Конечно они ВНУТРИ отличаются - ведь используются разные либы для доступа, но меня мало интересует что там внутри, я ведь репозитории создавал, чтобы знать, что получу наружу.
Поясняйте.
Ну я же говорю: есть у нас заказы, и есть у нас пользователи. Мне нужно выбрать пользователей, купивших тот или иной продукт. Сделать это проще всего через JOIN, если это касается РСУБД. Но есть у меня требование того, что пользователи и магазин - это два совершенно разных и как можно более независимых модуля. Я хочу иметь возможность более безболезненно отключить модуль магазина. Тогда что мне делать с JOIN'ом, жёстко прописанным в репозитории? Тут адаптеры мне на ум и приходят. Появляется хоть какая то надежда на независимость.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение BrusSENS »

samdark писал(а): 2017.09.04, 17:01 А чего не getUserWithOrders()?
Что если Shop и User два отдельных модуля? При отключении Shop, в UserRepository появится метод, который просто не нужен.
Upd: Или мы сменили хранилище ShopRepository на файлы, например?
Последний раз редактировалось BrusSENS 2017.09.04, 17:09, всего редактировалось 1 раз.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Repository vs ActiveRecord

Сообщение zelenin »

BrusSENS писал(а): 2017.09.04, 17:00 И вот появился вопрос, например, нужно выбрать заказы пользователя, я принципиально считаю, что сущности заказов не должны быть вложены в сущность пользователя, поэтому какая реализация подойдёт больше?
1. Выбираем заказы, передав в качестве аргумента OrderRepository->getAllByUser() ID или всю сущность?:

Код: Выделить всё

$user = $userRepo->gelById(1);
$orders = $orderRepo->getAllByUserId($user->getId());
// Или
$users = $userRepo->getById(1);
$orders = $orderRepo->getAllByUser($user);

$view->render(
     'user' => $user,
    'orders' => $orders
]);
Склоняюсь ко 2 варианту из-за возможного различия типа ID (int/string)

2. Выбираем все заказы всех пользователей, и передаём их как VO или по отдельности:

Код: Выделить всё

$users = $userRepo->getAll();
$orders = $orderRepo->getAllByUsers(Array::arrayFromPropertyValues($users, 'id'));

$view->render(
     'user' => $user,
    'orders' => $orders
]);

// или

$users = $userRepo->getAll();
$orders = $orderRepo->getAllByUsers(Array::arrayFromPropertyValues($users, 'id'));

$collection = new UserWithOrdersCollection($users, $orders);

$view->render(
     'collection' => $collection
]);
Опять же склоняюсь ко второму варианту, т.к. по идее это у нас получается некая коллекция.
ну это вообще не по теме. Тут нет repo vs AR, тут нет какой-либо проблемы с репо. Тут просто вопросы о вкусовщине. Как хотите так и делайте.
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение BrusSENS »

zelenin писал(а): 2017.09.04, 17:07 ну это вообще не по теме. Тут нет repo vs AR, тут нет какой-либо проблемы с репо. Тут просто вопросы о вкусовщине. Как хотите так и делайте.
Ага, понял.

А вообще хотелось бы что бы опытные девелоперы рассказали, на какие камни например натыкались, используя AR и Repository. Преимущества обоих паттернов понятны. Но вот сложности в реализации присутствуют по любому, вот и хотелось бы услышать, какие это сложности и как их решали.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Repository vs ActiveRecord

Сообщение zelenin »

BrusSENS писал(а): 2017.09.04, 17:05 Ну я же говорю: есть у нас заказы, и есть у нас пользователи. Мне нужно выбрать пользователей, купивших тот или иной продукт. Сделать это проще всего через JOIN, если это касается РСУБД. Но есть у меня требование того, что пользователи и магазин - это два совершенно разных и как можно более независимых модуля.
ок, хороший вопрос. Но у нас модуль продуктов знает, что есть юзеры. То есть как минимум они должны присутствовать в модуле продуктов. И общаться с модулем юзеров через адаптеры.
Соответственно ProductRepository имеет метод getUsersByProduct(), возвращающий User'ов модуля Product, через UsersByProductProviderInterface, реализованный для модуля User. Эта реализация будет знать о модуле User.
Можно и проще обойтись - у нас реализация ProductRepository будет не только основана на некоей ORM, но и на конкретном модуле User. Но это бОльшая связанность.
BrusSENS писал(а): 2017.09.04, 17:05 Я хочу иметь возможность более безболезненно отключить модуль магазина. Тогда что мне делать с JOIN'ом, жёстко прописанным в репозитории? Тут адаптеры мне на ум и приходят. Появляется хоть какая то надежда на независимость.
да, правильно приходят. Через адаптеры мы модули и связываем.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение ElisDN »

BrusSENS писал(а): 2017.09.04, 14:07 Пока делаете простые выборки (касаемо SQL) сущностей - всё круто и просто... нужно сделать JOIN и профильтровать поля - это просто превращается в поиск элегантного решения, которое очень сложно найти.
Разделите на Domain Model и Read Model:
viewtopic.php?f=34&t=42014&p=207524#p207524
BrusSENS писал(а): 2017.09.04, 14:07 ...когда нужно Lazy и Eager... Некоторые советуют "юзать прокси", насчёт этого я вообще промолчу.
Прокси достаточно удобны:
viewtopic.php?f=34&t=43009
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение BrusSENS »

ElisDN писал(а): 2017.09.04, 17:18 Разделите на Domain Model и Read Model:
viewtopic.php?f=34&t=42014&p=207524#p207524
Думал насчёт этого. Получается, что у нас
ElisDN писал(а): 2017.01.05, 21:49 на сайте стопицот виджетов и листингов
то появятся ещё и
ElisDN писал(а): 2017.01.05, 21:49 стопицот
репозиториев, подмена которых станет, скорее всего, неприятным делом.
ElisDN писал(а): 2017.09.04, 17:18 Прокси достаточно удобны:
viewtopic.php?f=34&t=43009
Да, читал Ваши статьи, но прокси оттолкнул сразу. Ну не понравилось, как реализовано, неявно что ли... Не знаю даже, как описАть.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Repository vs ActiveRecord

Сообщение zelenin »

BrusSENS писал(а): 2017.09.04, 17:34
ElisDN писал(а): 2017.09.04, 17:18 Разделите на Domain Model и Read Model:
viewtopic.php?f=34&t=42014&p=207524#p207524
Думал насчёт этого. Получается, что у нас
ElisDN писал(а): 2017.01.05, 21:49 на сайте стопицот виджетов и листингов
то появятся ещё и
ElisDN писал(а): 2017.01.05, 21:49 стопицот
репозиториев, подмена которых станет, скорее всего, неприятным делом.
ElisDN писал(а): 2017.09.04, 17:18 Прокси достаточно удобны:
viewtopic.php?f=34&t=43009
Да, читал Ваши статьи, но прокси оттолкнул сразу. Ну не понравилось, как реализовано, неявно что ли... Не знаю даже, как описАть.
так lazy это и есть неявная ленивая подгрузка.
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: Repository vs ActiveRecord

Сообщение BrusSENS »

zelenin писал(а): 2017.09.04, 17:47 так lazy это и есть неявная ленивая подгрузка.
Это понятно. Я про неявность самой реализации. В общем не понравилась реализация с прокси, хотя ничего пока стоящего, чем заменить в голову не приходит.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Ответить