Страница 1 из 2

Repository vs ActiveRecord

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

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

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

Так вот хотелось бы от вас услышать ваше мнение по поводу решения подобных проблем при разработке, может кто уже имеет элегантные решения, ведь сама идея, лично мне очень нравится, но вот всё портит сложность реализации подобных гибких выборок, связей и фильтров.

Re: Repository vs ActiveRecord

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

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

За гибкость приходится платить... за негибкость тоже :)

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 15:09
BrusSENS
samdark писал(а): 2017.09.04, 14:25 Да, репозитории писать бывает не просто и не быстро. Зато можно запилить оптимально и перепилить в одном месте, если что.
В том то и дело, что пока всё просто - всё хорошо. Опять же ясно, что серебряной пули не бывает.
samdark писал(а): 2017.09.04, 14:25 За гибкость приходится платить... за негибкость тоже :)
Это да. Поэтому и хочется, что бы знающие люди поделились своим опытом, т.к. встречающиеся абстрактные решения ничего общего с реальностью не имеют. Спецификации? Бред, как по мне, удобства не добавляют, лучше какой-то абстрактный билдер критерии сделать. И пять таки вопрос в том, как правильнее сделать передачу JOIN фильтров по другим репозиториям. Я в соседней теме своё видение связей представлял, но ни одного ответа так и не получил.

Re: Repository vs ActiveRecord

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

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 15:38
zelenin
BrusSENS писал(а): 2017.09.04, 15:09лучше какой-то абстрактный билдер критерии сделать
или не обязательно абстрактный и не обязательно билдер. Просто критерию. Можно под конкретный репозиторий написать, постепенно усложняя при необходимости. Я так сделал на текущем проекте.
BrusSENS писал(а): 2017.09.04, 15:09И пять таки вопрос в том, как правильнее сделать передачу JOIN фильтров по другим репозиториям.
непонятна проблема.

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 16:09
BrusSENS
zelenin писал(а): 2017.09.04, 15:38 или не обязательно абстрактный и не обязательно билдер. Просто критерию. Можно под конкретный репозиторий написать, постепенно усложняя при необходимости. Я так сделал на текущем проекте.
Если не сложно, можете пример привести? Просто в моём понимании, хорошая критерия - это билдер, неважно текучий или нет. Посему хочется увидеть, как ещё можно подойти.
zelenin писал(а): 2017.09.04, 15:38 непонятна проблема.
Например, есть у нас агрегат User, Order и Product. По логике User знать о Order и Product не должен. Но нам нужно вывести пользователей, которые фильтруются по некой таблице из агрегата Order, например, пользователей, заказавших некоторый Product. AR на позволяет юзать joinWith, вроде как, а вот что делать в случае, когда у нас получение всех сущностей происходит через репозиторий? Надеюсь понятно объяснил.

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 16:18
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, не надо в данном контексте упоминать другие агрегаты и репозитории. Т.е. ничего пробрасывать не надо.

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 16:42
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, не надо в данном контексте упоминать другие агрегаты и репозитории. Т.е. ничего пробрасывать не надо.
Это то понятно, что таблицы у нас тут не при чём. Но всё таки нужно быть реалистами. Репозитории работающие с РСУБД в качестве хранилища будут отличаться от репозиториев для отличных от РСУБД хранилищ.

Re: Repository vs ActiveRecord

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

У меня например есть под рукой два приложения - на postgres и на mongo. Конечно они ВНУТРИ отличаются - ведь используются разные либы для доступа, но меня мало интересует что там внутри, я ведь репозитории создавал, чтобы знать, что получу наружу.
Поясняйте.

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 17:00
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
]);
Опять же склоняюсь ко второму варианту, т.к. по идее это у нас получается некая коллекция.

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 17:01
samdark
А чего не getUserWithOrders()?

Re: Repository vs ActiveRecord

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

У меня например есть под рукой два приложения - на postgres и на mongo. Конечно они ВНУТРИ отличаются - ведь используются разные либы для доступа, но меня мало интересует что там внутри, я ведь репозитории создавал, чтобы знать, что получу наружу.
Поясняйте.
Ну я же говорю: есть у нас заказы, и есть у нас пользователи. Мне нужно выбрать пользователей, купивших тот или иной продукт. Сделать это проще всего через JOIN, если это касается РСУБД. Но есть у меня требование того, что пользователи и магазин - это два совершенно разных и как можно более независимых модуля. Я хочу иметь возможность более безболезненно отключить модуль магазина. Тогда что мне делать с JOIN'ом, жёстко прописанным в репозитории? Тут адаптеры мне на ум и приходят. Появляется хоть какая то надежда на независимость.

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 17:06
BrusSENS
samdark писал(а): 2017.09.04, 17:01 А чего не getUserWithOrders()?
Что если Shop и User два отдельных модуля? При отключении Shop, в UserRepository появится метод, который просто не нужен.
Upd: Или мы сменили хранилище ShopRepository на файлы, например?

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 17:07
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, тут нет какой-либо проблемы с репо. Тут просто вопросы о вкусовщине. Как хотите так и делайте.

Re: Repository vs ActiveRecord

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

А вообще хотелось бы что бы опытные девелоперы рассказали, на какие камни например натыкались, используя AR и Repository. Преимущества обоих паттернов понятны. Но вот сложности в реализации присутствуют по любому, вот и хотелось бы услышать, какие это сложности и как их решали.

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 17:14
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'ом, жёстко прописанным в репозитории? Тут адаптеры мне на ум и приходят. Появляется хоть какая то надежда на независимость.
да, правильно приходят. Через адаптеры мы модули и связываем.

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 17:18
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

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 17:34
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
Да, читал Ваши статьи, но прокси оттолкнул сразу. Ну не понравилось, как реализовано, неявно что ли... Не знаю даже, как описАть.

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 17:47
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 это и есть неявная ленивая подгрузка.

Re: Repository vs ActiveRecord

Добавлено: 2017.09.04, 18:05
BrusSENS
zelenin писал(а): 2017.09.04, 17:47 так lazy это и есть неявная ленивая подгрузка.
Это понятно. Я про неявность самой реализации. В общем не понравилась реализация с прокси, хотя ничего пока стоящего, чем заменить в голову не приходит.