data-mapper или active-record

WMix

герр M:)ller
Партнер клуба
у меня возможно глупый вопрос

представим сущность типа человек
PHP:
class Person{

  private $name;

  public finction __construct($name){
    $this->name = $name;
  }
}
/* @var $person Person */
$person = $mapper->getPersonById(42);
к примеру нужно получать подобные обьекты через relation пусть отец или дети

влепив прокси получилось подобное
PHP:
class Person{
  private $name;

  /** 
   * @var Person
   */
  private $father;

  public finction __construct($name, Proxy $father){
    $this->name = $name;
    $this->father = $father;
  }
}
$father = $person->father();
но вдруг мне подумалось что получился active-record, что правильно через mapper достать типо
PHP:
$father = $mapper->father($person);
и это упрощает очень многое. как правильно с точки зрения data-mapper получать подобные обьекты?
 

WMix

герр M:)ller
Партнер клуба
@Adelf, я несколько иначе представлял "aggregate root". ну те да у меня есть отец, у него есть тоже отец и тд. и в данном случае "я" являюсь корнем этого агрегата. те эта часть понятная. непонятно как правильно создать этот обьект "я". вытянуть каждую персону отдельно и сложить вместе или на месте relation создать обьект заглушку которая при надобности разберется как добыть обьект и все это рекурсивно. вернее вопрос в том какой из этих двух подходов верный чтоб назвать это data-mapper.
@Вурдалак, меня не отец интересует. в данном случае это пример "self-relation" ну пусть будет в задаче узнать его болезни чтоб просчитать возможную передачу по наследству.
 

WMix

герр M:)ller
Партнер клуба
на какой обьект переложить задачу "рекурсивно создавать". это на уровне сервиса или на уровне маппера?
 

Вурдалак

Продвинутый новичок
@Вурдалак, меня не отец интересует. в данном случае это пример "self-relation" ну пусть будет в задаче узнать его болезни чтоб возможную просчитать передачу по наследству.
Это не совсем то, что я хотел узнать. Я имел в виду это нужно для отображения или для внутренного поведения сущности при изменении? Просто «мне нужно получить relation» для меня недостаточно, чтобы можно было корректно ответить.

В любом случае, в контексте среднестатистического говнокода, разницы между Proxy $father и $mapper->father() я не вижу вообще. Получил объект $father? Молодец, ты выполнил свою задачу.
 

WMix

герр M:)ller
Партнер клуба
задача разобраться, научиться.
это внутреннее поведение вероятнее всего, после манипуляций обьект "я" будет записан, и рекурсивно запишутся изменения если таковые были и у зависимых обьектов, хотя отображение мне также интересно, чтоб понять разницу в этих подходах.
 

Вурдалак

Продвинутый новичок
задача разобраться, научиться.
это внутреннее поведение вероятнее всего, после манипуляций обьект "я" будет записан, и рекурсивно запишутся изменения если таковые были и у зависимых обьектов, хотя отображение мне также интересно, чтоб понять разницу в этих подходах.
Проблема в том, что для меня озвученной проблемы не существует вообще. Я не могу вспомнить случая, когда мне действительно нужна рекурсивная зависимость. Ну, разве что для отображения. При этом ты упомянул пример с наследованием признаков от родителей. И это неплохой пример. Дело в том, что ребёнок на самом деле никак напрямую с родителями не связан. Если что-то станет с родителем, то на самого ребёнка физически это никак не повлияет и наоборот. Ребёнок — это отдельный самодостаточный объект, в котором будут скопированные с родителей признаки с какими-то мутациями. В ребёнке нет ссылок на объекты родителей.
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
я правильно понимаю: в "нормальной" программе, созданная сущность на конкретную задачу полностью завершенная, атрибуты заглушки это признак плохого кода?
 

Вурдалак

Продвинутый новичок
я правильно понимаю: в "нормальной" программе, созданная сущность на конкретную задачу полностью завершенная, атрибуты заглушки это признак плохого кода?
Если говорить о тех подходах, которыми предпочитаю пользоваться я, то, выражаясь коротко, ссылка на сущность, которая находится «за пределами» корневой сущности, называемой aggregate root, действительно считается проблемой. В общем случае, здесь уже нельзя будет гарантировать какую-либо целостность. Допустим, если сущность A содержит внутри ссылку на сущность B и если у нас будет прямая возможность влиять на B в обход A, то при параллельных изменениях сущность A может вести себя некорректно, полагаясь на состояние B, которое на этот момент уже было изменено. Тут либо надо передавать все необходимые данные в методы сущности напрямую, либо инкапсулировать сущность B в сущности A, не давая никому на неё ссылку.

Можно опять обратиться к реальному миру. Ребёнок может как-то реагировать на поведение родителей. В твоём подходе ребёнок об этом получит информацию откуда-то «изнутри», словно у него есть какая-то мистическая душевная связь с ними. В реальности же ребёнок получит информацию посредством внешних органов чувств: он может увидеть или услышать и на основе этого он как-то внутренне поменяться, отреагировать на увиденное/услышанное. Ты тут можешь возразить, что если ты передашь информацию через метод, то где гарантия, что это действительно признаки его родителей? Ну, ведь и в реальности так же: ребёнок может увидеть брата-близнеца отца, но если он не знает про его существование, то он будет реагировать на это, словно это его отец.
 

fixxxer

К.О.
Партнер клуба
Я попробую переформулировать вопрос WMix-а, мне кажется, я понимаю, о чем он.
Представим себе такую domain model, где естественным представлением является такое: aggregate root - корень дерева, и далее огромное количество деток любой вложенности. При этом дерево слишком большое, чтобы держать его в памяти целиком.
 

Redjik

Джедай-мастер
у домена нет представления
вы вроде как путаете домен и secondary port adapter

ЗЫ. а вообще решается проксей конкретно этот пример, в той же доктрине оно есть
 

Вурдалак

Продвинутый новичок
Я попробую переформулировать вопрос WMix-а, мне кажется, я понимаю, о чем он.
Представим себе такую domain model, где естественным представлением является такое: aggregate root - корень дерева, и далее огромное количество деток любой вложенности. При этом дерево слишком большое, чтобы держать его в памяти целиком.
Нужен более мелкий aggregate root. Ну, или увеличить память.
 

WMix

герр M:)ller
Партнер клуба
а вообще решается проксей конкретно этот пример, в той же доктрине оно есть
у меня было такое же представление, но эта прокся по сути и есть заглушка, а цикличность проявляется везде. заказ содержит позиции, позиции продукт, продукт поставщиков и тд можно напридумывать так что опять к заказу придешь.
ну и в процессе отлаживания ты дампишь ее. И вместо нормального человеческого языка ты видишь что аттрибут отец это прокси у которого есть ссылка на всякие мапперы, репы и база тоже там, такой дамп страниц на надцать...
самой сущности по идеи хранить ссылку на всю эту мишуру не надо, как сохранить ее решает опять же маппер ссылка на который также присутствует в другой переменной.
с другой стороны, не имея прокси "lazy" у меня либо не законченные обьекты (те один из отцов не содержит больше отца), либо я гружу всю базу в память (где терминатор?)

вот я и ищу ошибку в своих размышлениях
 

Вурдалак

Продвинутый новичок
у меня было такое же представление, но эта прокся по сути и есть заглушка, а цикличность проявляется везде. заказ содержит позиции, позиции продукт, продукт поставщиков и тд можно напридумывать так что опять к заказу придешь
Ты так и не ответил на главный вопрос: зачем вообще хранить ссылку на объект, а не просто id зависимого объекта? ООП — это не про правильные иерархии объектов, а про поведение. Как это влияет на поведение заказа? Ну, позиции заказа — я могу понять. Продукт целиком — уже нет. Допустим, что что-то там может повлиять. Почему бы не скопировать нужные свойства продукта и поставщика в заказ? Объясни мне, как человеку, никогда не писавшему корзины с заказами для интернет-магазинов. Только на как можно более конкретном примере без «ну, а что если поставщика поменяют, а заказ так и останется со скопированными данными?!».
 

WMix

герр M:)ller
Партнер клуба
мне сложно ответить на этот вопрос, я сам пытаюсь понять. на счет id, его я собирася его убирать из сущности (опционально можно не писать), мол identity ничего общего с доменом не имеет (есть номер заказа, возможно он даже уникален но id это часть хранения в базе и о нем только "база" и знает), тактм же образом поступил и с внешними колючами (collection). получился удобный обект с атрибутами описывающими человеческую жизнь. и вроде все работает, обьекты достаются и сохраняются...

меня смущает слово "поведение", как будто я думаю о чемто другом, представляя себе сущность.
у заказа есть метод который просчитает сумму (это об этом?). сумма считается не только от цены продукта которая у каждого клиента или поставщика разная но и от суммы налогов на химикаты, которые содержат некоторые продукты, она зависит и от партии каждого из продуктов. (5 шт продукта А по 5руб, 100шт по 4,5) она также зависит от общей суммы веса или обьема (транспорт).

поставщик это склад в регионе клиента, врятли он поменяется, в любом случае не на заказ, и конечно все как было так и останется
 

Вурдалак

Продвинутый новичок
мол identity ничего общего с доменом не имеет
Получается, что когда пользователь делает выбор между «вот тем» продуктом» и «вот тем», он делает это с помощью телепатии, а не указывая id?

есть номер заказа, возможно он даже уникален но id это часть хранения в базе и о нем только "база" и знает
Если у тебя номер заказа и так отлично подходит в роли id, то зачем тебе ещё один id?

меня смущает слово "поведение", как будто я думаю о чемто другом, представляя себе сущность.
Поведение — это реакция сущности на какой-то сигнал, называемый командой, с учётом текущего её состояния. Грубо говоря, (aggregate root, command) -> (aggregate root, events).

Наиболее «жирный» пример — это какая-нибудь игра. Есть много игр, где действия выполняются строго последовательно (всякие настольные игры, карточные и т.д.), которые можно легко уместить в один объект: там будет информация по игрокам на текущий момент, количеству их денег/карт/фишек. Этот объект — aggregate root. Через него мы выполняем различные действия типа «бросить кости». Результат может очень сильно отличаться в зависимости от текущего состояния, которое в настольных играх зачастую очень богатое. К примеру, игрока по результату бросания костей может перекинуть на поле, которое его мгновенно обанкротит, если у него нет нужного количества золота, а может, наоборот, дать ему какой-то артефакт и прочее.

Заказ, конечно, скорее всего обладает гораздо более скудным поведением. Я даже не уверен, что стал бы вот это описывать в самой сущности заказа:
у заказа есть метод который просчитает сумму (это об этом?). сумма считается не только от цены продукта которая у каждого клиента или поставщика разная но и от суммы налогов на химикаты, которые содержат некоторые продукты, она зависит и от партии каждого из продуктов. (5 шт продукта А по 5руб, 100шт по 4,5) она также зависит от общей суммы веса или обьема (транспорт)
Я бы, вероятно, просто передавал итоговую сумму, избавляя заказ от таких подробностей, но не уверен. Но даже если допустить, что ты горишь желанием посчитать это внутри заказа, то ты можешь передать всё необходимое через метод (сервис или отдельные коэффициенты, правила и прочее), для этого не нужно брать данные откуда-то «изнутри».

P.S. Честное слово, так уже задолбал этот пример с заказами и Интернет-магазинами. Никто больше ничем интересным не занимается? :)
 

WMix

герр M:)ller
Партнер клуба
он делает это с помощью телепатии, а не указывая id
идея была такая:
на уровне middleware (url типа /order/{id}) создается готовый заказ. далее действие /order/{id}/add/product/{id}

те на уровне контролера мы имеем все что записано в руте стало обьектом остальное в request

PHP:
public function addAction( Order $order, Product $product, Request $r){
   // эта часть будет в сервисе
   $order->addProduct($product, $r->post('qty',1) );
   $ordermapper->store( $order );
}
в этот момент появится событие что заказ изменен что ведет например к пересчету суммы, изменению состояния склада (резервирование)
каждое из вышеупомянутых действий я уже делалал хотябы по отдельности.

Если у тебя номер заказа и так отлично подходит в роли id, то зачем тебе ещё один id?
ну не совсем, номер заказа уникален на поставщика, связка company_group_nr + company_nr + order_nr сложна и на этом уровне не интересна.
 
Последнее редактирование:
Сверху