BlackFox — PHP фреймворк для веб-сайтов и приложений

Adelf

Administrator
Команда форума
Про Redux-подобные фронтенды завтра расскажу, много букв. Это все удивительно похоже на CQRS. Только на клиенте.
Все нормальное на CQRS похоже. Сама природа чтения и записи всегда разная. и идея Redux на фронте мне в свое время очень понравилась - ясные и четкие потоки данных, нормально контролируемые.
 

Вурдалак

Продвинутый новичок
Ага, сэкономили 50 байт на запросе, а потом втащили какой-нибудь jquery ui весом в пару мегабайт. :) Ну не знаю, экономия на спичках это все.
Да это в общем-то не для снижения трафика, а чтобы клиенты имели мотивацию старое говно выпиливать, чтобы потом можно было без нарушения BC удалять старые элементы API.
В случае web тут всё более-менее просто, а мобильные приложения имеют медленный цикл обновления.

При отсутствии контроля, клиенты запрашивают тонны информации и сами не знают для чего.

Мне нравится, что есть какие-то механизмы давления на клиентов. Там можно поля @deprecated помечать, например, а потом с определенной версии билда (клиентского билда, в смысле) можно тупо запретить использовать эти поля. Наличие таких договоренностей заставляет клиенты со своей стороны проводить ревизию используемых методов.
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
По поводу Redux вспомнил, что есть такая штука как Redux Saga. Это не имеет ничего общего с Saga'ами из Event Sourcing'а и приходилось каждый раз гуглить «saga -redux».
 

Adelf

Administrator
Команда форума
По поводу Redux вспомнил, что есть такая штука как Redux Saga. Это не имеет ничего общего с Saga'ами из Event Sourcing'а и приходилось каждый раз гуглить «saga -redux».
А почему ты связываешь саги и ЕС ? ) Разные же совсем понятия
 

флоппик

promotor fidei
Команда форума
Партнер клуба
Блин, вот капитанская же вообще идея, почему надо потратить 10 лет чтобы это дошло?
На самом-то деле, папка ООП Алан Кей сразу писал:


More recently people keep claiming that OOP “isn’t really” about classes and objects and the important bit is actually the messages. In the 1998 post, after saying how much he regrets “objects”, Kay also says that “the big idea is ‘messaging’”. He later follows up with:

«OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I’m not aware of them.»
 

Adelf

Administrator
Команда форума
Ну... саги по мне это просто вариант реализации распределенных транзакций. А ЕС - это вариант хранить стейт. Сага может состоять из микросервисов... какие-то из них могут юзать ЕС, какие-то нет...
То, что и там и там эвенты - это конечно делает их близкими. Но и все.
 

Вурдалак

Продвинутый новичок
А почему ты связываешь саги и ЕС ? ) Разные же совсем понятия
В принципе, не совсем корректно с моей стороны связывать их напрямую, но ES и саги созданы друг для друга и тут невероятная синергия.

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

Сага обычно запускается по событию... которое может не дойти.
Сага в принципе ждёт события, которые могут не дойти.
Сага должна понимать не дубликат ли события перед ней (по eventId, который обычно UUID). Это возможно только если события реально пересистятся. Считать, что события одинаковые, если полностью одинаковый payload некорректно. События — это не value objects. Это immutable data objects, имеющие свой идентификатор.
Сага может выполнить команду, но она должна использовать какой-то ключ идемпотентности, что если вдруг всё рухнуло, сага попыталась бы выполнить именно ту же команду, а не новую (иначе может прийти к тому, что будет несколько INSERT или что-то в в этом духе).
В ответ сага получит список событий от команды. Но вот в чём прикол: получается, факт выполнения команды должен записываться в той же транзакции, что aggregate root и события (если просто события в случае чистого ES). Опять-таки появляется необходимость иметь персистентные события (которые правда не обязаны должно жить).

Сделать такую Сагу, которая будет способна восстановиться после падения на любом из этапов без ES (и его имитаций) мне представляется крайне сложной задачей.

Ну, и по поводу связывания в принципе... Саги — это стандартный ответ на вопрос всех интересующихся DDD/CQRS/ES: «а что если мне нужно сохранить в транзакции несколько aggregate root». Это естественное продолжение идеи о том, что обеспечить согласовать всех данных на любой момент времени в крупном проекте невозможно: это всё про eventual consistency. Я, честно говоря, не слышал про них вне этой тематики.
 

Вурдалак

Продвинутый новичок
Сага может состоять из микросервисов... какие-то из них могут юзать ЕС, какие-то нет...
Эммм. Сага — обычно такой своеобразный aggregate root (который нередко делают event sourced, btw). Только вместо `->apply(new Event())` обычно делает `->send(new Command())`.

А если события не персистятся, то будет утеряна информация о том произошло ли что, чем ожидает сага в случае любого факапа. Причем, как я отметил выше, события должны персиститься в той же транзакции, что и состояние aggregate root, иначе опять может возникнуть ситуация, когда изменения вступило в силу, а факта в виде события больше нет.
 

Вурдалак

Продвинутый новичок
Правда если подходить к вопросу о том, что такое сага из первоисточника, то в принципе можно условиться, что сагой может называться просто персистеный объект с флагами под каждую транзакцию, который умеет дергать идемпотнентные команды-транзакции (которые возвращают true/false) и выполнять компенсирующие транзакции в обратном порядке, если какая-то транзакция сфейлилась.

Но в современной архитектуре всё чаще признаком успеха команды являются события, что как бы подводит нас к несколько более сложной реализации.

Плюс некоторые события являются не результатом выполнения команды от имени саги, а как результат изменения некоторого aggregate root. Например, что пришёл платеж. Мы не можем выполнить транзакцию «приведи мне платёж». Платеж придёт тогда, когда он сможет. Факт платежа — событие, которое мы не должны потерять.
 

Adelf

Administrator
Команда форума
По крайней мере, я говорю про такую сагу, которая слушает события и выполняет команды (хотя может и публиковать события тоже).
Это наверно Orchestration-based saga. Есть еще Хореография based :) но вроде они отмирают потихоньку. https://microservices.io/patterns/data/saga.html

Читая тебя я сразу вспомнил эту статейку - https://www.innoq.com/en/blog/domain-events-versus-event-sourcing/ Там он правда сильно уперся в инфраструктуру...
 

Вурдалак

Продвинутый новичок
Это наверно Orchestration-based saga. Есть еще Хореография based :) но вроде они отмирают потихоньку. https://microservices.io/patterns/data/saga.html
Ну так опять встаёт вопрос отказоусточивости.

Допустим, Order из примера по ссылке публикует событие OrderCreated.
Что делать, если до остальных aggregate root событие не дошло (лежала база, посередине кода был exit, etc.)?
 

Adelf

Administrator
Команда форума
Идею с персистом события в одной транзакции я понял. Но это ж обычные, можно сказать инфраструктурные данные, типа обработано событие или нет. Какой тут эвент сорсинг если базы у участников саги могут быть разные?
 

Вурдалак

Продвинутый новичок
Читая тебя я сразу вспомнил эту статейку - https://www.innoq.com/en/blog/domain-events-versus-event-sourcing/ Там он правда сильно уперся в инфраструктуру...
Там много ерунды.
In addition, each event only contains the state that is necessary to be able to reconstruct the state of the aggregate during replay. This is typically only that state that has been influenced by the invocation that triggered the event, that is, a kind of “diff”. From the point of view of event sourcing, it makes no sense to store additional state on an event that was not influenced by the invocation. So, even if an explicit event UserRegistrationCompleted were persisted, it would not contained any additional state.
Вот у нас там стоит сейчас код в самом AR:
PHP:
public function provideUserDetails(...): void
{
    // ...

    $this->checkIfRegIsComplete();
}

private function checkIfRegIsComplete(): void
{
     if (
        $this->phoneNumber
        && $this->phoneNumberValidated
        && $this->userDetails
        && $this-completed === false // Вот этот флаг меняется где-то в мутаторе события UserRegistrationCompleted
    ) {
        $this->apply(new UserRegistrationCompleted($this->userId));
    }   
}
В дальнейшем требования к регистрации могут поменяться, а факт того, что когда-то этот юзер прошел регистрацию (UserRegistrationCompleted) при актуальных на тот момент требованиях, так и останется.

Событие в ES не предназначены сообщать о фактах, а не для какой-то там передачи состояния в первую очередь.
So, even if an explicit event UserRegistrationCompleted were persisted, it would not contained any additional state.
«Состояние» тут заключается в самом факте завершения регистрации и меняет флаг completed в мутаторе.

process these fine-granular events and know at least parts of the domain logic from the
...
ignore events which are not of any interest in the respective bounded context

combine several events to get the whole state required about the user

Domain event — это любой event, который может быть трактован в терминах бизнеса. Это может быть хоть event из самого aggregate root, а может быть создан в результате аггрегации event stream'а для внешних bounded context'ов.
 

Вурдалак

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

У меня есть сейчас 2 типа aggregate root'ов: которые пересистят и пересобираются из событий (чистый ES), и те, которые пересистят и стейт и некоторые события. Если я буду пересистить все события в последнем случае и не удалять их, то я всегда смогу пересобрать состояние aggregate root, т.е. это будет тот же ES, только как если бы я хранил снепшот после каждого изменения. То есть второй тип aggregate root внутри модели вообще от ES aggregate root не отличается, я просто храню подмножество событий и какое-то время.

UPD: даже не 2 типа aggregate root'ов, а 2 типа репозиториев, поскольку, как я уже сказал, по коду aggregate root невозможно сказать как он персистится.

Но я уже вроде сказал, что строго говоря, никакой прямой связи между ES и сагами нет, просто существует очень мощная синергия. Что я еще могу добавить? :)
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Но это ж обычные, можно сказать инфраструктурные данные, типа обработано событие или нет. Какой тут эвент сорсинг если базы у участников саги могут быть разные?
Я попробую пояснить.
События персистятся вместе с aggregate root. Не просто «обработано или нет», персистится само событие, с event id, payload, etc.
Сага тут вообще не причем, она может быть на другом сервере.
Потом события сливаются в публичные стримы, из которых уже могут их читать саги или какие-то агрегаторы статистики.
Технически, таблица с событиями у «обычного» (не-ES) aggregate root фактически ничем не отличается от таблицы событий реального aggregate root.
«Стейт» у обычного AR в ES называется «Snapshot».
Тут возникает достаточно условное деление на ES и нет: если подкрутить настройки «обычного» aggregate root так, чтобы персистились все события и не удалялись, то это будет полноценный ES aggregate root.

Saga важны случившиеся факты в виде событий.
Чтобы эти факты гарантированно были доставлены, нам нужно эти факты начать хранить.
Когда ты начинаешь хранить события, то остановиться будет трудно.
Все говорят, что они могут в любой момент остановиться.
Но потом появляется зависимость.
Хранить лишь часть событий будет уже недостаточно, возрастет толератность.
В общем, именно поэтому я так неосторожно упомянул event sourcing.
 
Сверху