Как реализуете репозиторий (Doctrine, Analogue, ручками, etc)

AmdY

Пью пиво
Команда форума
Ничего себе у тебя бомбит. Это что-то личное?
Почему бомбит, я сразу же указал на проблему. Как и пару раз до этого.

При этом ты сам же тут же подтвердил мои слова, что это не лучший код.
Это какой-то недоделанный пример для какой-то презентации
 

Юрий Быков

Новичок
А для чего же тогда используете ES-модели? Или имелось ввиду модели без ES-инфраструктуры, а в репозиториях получаем все события и в рамках транзакции пишем в БД, например, нативным SQL или датамэппером.
Если так, то получается красиво, используем доменную модель, соблюдаем SRP, можно любую глубину собрать по связям моделей, события выстраиваются в линию, и в персистентности у нас чистый оптимизированный SQL без оверхеда.
 

fixxxer

К.О.
Партнер клуба
Doctrine 2.5 по мне более элегантный и функциональный нежели Analogue.
Несомненно. Код analogue проще понять именно потому что он небольшой. Он и не претендует на прям полноценный DM, это такая минималистичная реализация самых основных кейсов.

люди очень большое значение придают тому, как должно сохраняться
Скорее просто неохота вручную делать закат солнца. Хочется persist() и всё, а не фигурно выпиливать SQL лобзиком.
 

AmdY

Пью пиво
Команда форума
А для чего же тогда используете ES-модели? Или имелось ввиду модели без ES-инфраструктуры, а в репозиториях получаем все события и в рамках транзакции пишем в БД, например, нативным SQL или датамэппером.
Если так, то получается красиво, используем доменную модель, соблюдаем SRP, можно любую глубину собрать по связям моделей, события выстраиваются в линию, и в персистентности у нас чистый оптимизированный SQL без оверхеда.
Остаётся написать инструменты, которые будут это красиво и просто делать.
 

Вурдалак

Продвинутый новичок
А для чего же тогда используете ES-модели? Или имелось ввиду модели без ES-инфраструктуры, а в репозиториях получаем все события и в рамках транзакции пишем в БД, например, нативным SQL или датамэппером.
Да, имелось в виду это.

Причин тут несколько.

Во-первых, именно модель знает о том, что же там внутри нее изменилось. Поэтому это логично записывать события внутри нее, а не снаружи. Это пока не требует именно ES-подхода, но очень близко к нему (мы просто записываем ->recordThat(new EventWasBuzzed() события по мере необходимости) .

Во-вторых, повышается риск расхождения между состоянием модели и событиями, если мы их записываем независимо. В ES-модели это невозможно в силу того, что изменения происходят тогда и только тогда, когда есть событие. Нет событий — никто эти изменения не увидит. Есть события — они обязательно повлияют на состояние модели.

В-третих, у нас была инфраструктурная проблема. Нужно было простое и понятное получение изменений модели. Дело в том, что если маппинг сложный и/или Doctrine по каким-то причинам не подходит для маппинга domain model напрямую, то получение диффа превращается в непростую задачу. Даже получив дифф (в виде массива свойств, которые изменились), требовались какие-то дополнительные проверки того, что именно привело к изменению, потому что в базе данные по разным причинам иногда хранятся по разному. Т.е. в модели может быть статус + какой-то флаг, а в базе это может хранится в виде одного числа. Повышался риск того, что реальные изменения в модели не будут соответствовать тем, что пойдут в базу. С явным же списком событий эта вероятность существенно снижается.

Есть ещё ряд некоторых неочевидных плюсов, но это основные.
 

fixxxer

К.О.
Партнер клуба
Почему бомбит, я сразу же указал на проблему. Как и пару раз до этого.
А эти проблемы не имеют никакого значения, поскольку не имеют отношения к демонстрируемым концепциям. Там на это вообще не надо смотреть.
 
  • Like
Реакции: AmdY

Юрий Быков

Новичок
Во-первых, именно модель знает о том, что же там внутри нее изменилось. Поэтому это логично записывать события внутри нее, а не снаружи. Это пока не требует именно ES-подхода, но очень близко к нему (мы просто записываем ->recordThat(new EventWasBuzzed() события по мере необходимости).
Получается в контексте TripPlanner при создании/обновлении/удалении нового маршрута нужно пробежать по всей глубине объектов (Route, Leg и т.д.) и собрать ивенеты со всех объектов, коллектора у нас нет?
 

Вурдалак

Продвинутый новичок
Получается в контексте TripPlanner при создании/обновлении/удалении нового маршрута нужно пробежать по всей глубине объектов (Route, Leg и т.д.) и собрать ивенеты со всех объектов, коллектора у нас нет?
Тут лучше либо записывать события всегда в aggregate root, либо иметь какой-то EventHistory, инстанс которого передавать в дочерние объекты.

Всё равно чтобы добавить новый этап маршрута (Leg), тебе понадобится выполнять метод через Trip: $trip->enlargeRouteWithLocation($routeId, $date, $location), поэтому обычно первого варианта достаточно.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
что в итоге приводит к функциональному программированию на промисах, которые пробрасываются 20 раз из объекта в объект, обрастая по пути все новыми обработчиками :)
я имею счастье такое дебажить
 

fixxxer

К.О.
Партнер клуба
Зачем?
У тебя в роутере веб-запросов же нет пачки промисов. Не вижу принципиальной разницы.

Меня в таком подходе смущает другое - при событийном подходе получается довольно сложный flow control, и в какой-то момент уже фиг поймешь, кто на ком стоял. Разве что изолировать события в рамках одного небольшого контекста.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
это немного шутка - в php нет асинхронных операций с промисами, которые непредсказуемо резолвятся, и все, действительно, можно задекорировать за Aggregate Root,
а если пойти дальше и развивать эту логику - получается ком событий, который обрастает обработчиками
 

Вурдалак

Продвинутый новичок
Меня в таком подходе смущает другое - при событийном подходе получается довольно сложный flow control, и в какой-то момент уже фиг поймешь, кто на ком стоял. Разве что изолировать события в рамках одного небольшого контекста.
Это уже скорее проблема монолитных репозиториев и отсутствия bounded contexts, так что в плане изоляции ты прав.

Но без конкретных примеров тут что-либо говорить по существу сложно. Это можно так махать руками и говорить, что, например, ООП — это сложно, объект на объекте, объектом погоняет, получается какой-то непонятный ком объектов, который обрастает взаимосвязями и методами. Мы-то знаем, что на практике это просто обозначает, что человек не умеет в ООП, что у него скорее всего просто куча архитектурных проблем, которые в силу своей некомпетентности он не замечает и сваливает это всё на ООП.
 

fixxxer

К.О.
Партнер клуба
Ту же мысль в голове держал, когда писал, но не стал уточнять. Поинт скорее в том, что с классами-интерфейсами-методами у нас есть IDE, а тут навигация опять ручками получается.

Щас пришла в голову другая мысль: у Кея ООП был в обмене сообщениями между объектами, и это все очень напоминает реализацию smalltalk style вызовов на java style language, не знаю, к чему я это даже :) Ну, конечно, в ST не так, я скорее про саму идею изначальную (объекты меняются сообщениями в соответствии с контрактами, и на этом всё).
 

Вурдалак

Продвинутый новичок
В командо-событийной архитектуре есть свои концепции, упрощающие жизнь (saga, process manager), которые слушают события и отправляют команды. И, вероятно, если у нас непонятный ком из событий, то скорее всего мы не смогли просто разобраться в бизнес-логике и правильно выделить и описать процессы, происходящие между моделями.
 

Юрий Быков

Новичок
Так понимаю ES-модель нарушает SRP, т.к. приходиться в рут аггрегате иметь инстанс накопителя событий и передавать его дочерним объектам, т.е. мы и за Entity отвечаем и знаем что есть коллектор событий.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
ES-модель нарушает SRP, т.к. приходиться в рут аггрегате иметь инстанс накопителя событий и передавать его дочерним объектам, т.е. мы и за Entity отвечаем и знаем что есть коллектор событий.
Event и SRP описывают отношения объектов на разных уровнях, одно никак другому не мешает
 

Юрий Быков

Новичок
Если это было бы автоматически сгенерировано, например, при использовании AOP, то нормально, но так сущность должна знать что у неё есть еще одна обязанность помимо бизнес-логики, это обработка Event, т.е. я не могу просто создать сущность и работать с ней, мне нужно внедрить это обработку.
 

fixxxer

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

grigori

( ͡° ͜ʖ ͡°)
Команда форума
сущность должна знать что у неё есть еще одна обязанность помимо бизнес-логики, это обработка Event, т.е. я не могу просто создать сущность и работать с ней, мне нужно внедрить это обработку.
Объект - это объединение данных и логики. Логика статична, а ее исполнение определяется событиями. Так объекты "реагируют" на внешние события.
Единственая сущность в природе, которая не реагирует на события - это черные дыры. Не надо их создавать :)
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Так понимаю ES-модель нарушает SRP, т.к. приходиться в рут аггрегате иметь инстанс накопителя событий и передавать его дочерним объектам, т.е. мы и за Entity отвечаем и знаем что есть коллектор событий.
Здесь всё зависит от того, как ты определяешь «ответственность». Ответственность модели — моделировать понятие из предметной области (как ни странно). События, как часть этого процесса моделирования — лишь деталь реализации.

И забей на «передавать дочерним объектам», так обычно никто не делает, события записываются обычно только в aggregate root.

Выглядит это как-то так: https://github.com/qandidate-labs/broadway/blob/master/examples/event-sourced-domain-with-tests/Invites.php#L44
 
Сверху