DDD, Saga и Event-Sourcing: может ли действие компенсации просто быть удалено из хранилища событий?

15

Я понимаю, что вышеупомянутый вопрос, вероятно, поднимает несколько вопросов «что?», Но позвольте мне попытаться объяснить:

Я пытаюсь обдумать несколько взаимосвязанных концепций, в основном шаблон Saga ( http://www.rgoarchitects.com/Files/SOAPatterns/Saga.pdf ) в сочетании с Event-sourcing (DDD-концепция). : http://en.wikipedia.org/wiki/Domain-driven_design )

Хороший пост, который объединяет его: https://blog.jonathanoliver.com/cqrs-sagas-with-event-sourcing-part-ii-of-ii/

Я подойду к вопросу через минуту, но я думаю, что сначала я должен попытаться суммировать то, что я понимаю о нем (что может быть неправильно, поэтому, пожалуйста, исправьте, если это так), так как это может повлиять на то, почему я задать вопрос для начала:

  1. Шаблон Saga - это своего рода брокер, который дает действию (конечному пользователю, автоматизированному и т. Д. Практически все, что собирается изменить данные), разделяет это действие в бизнес-действиях и отправляет каждое из этих действий в виде сообщений в шину сообщений, которая в свою очередь отправляет его в соответствующие совокупные корни, о которых нужно позаботиться.
  2. Эти совокупные корни могут работать полностью автономно (хорошее разделение задач, отличная масштабируемость и т. Д.)
  3. Сам Saga-экземпляр не содержит никакой бизнес-логики, которая содержится в агрегатных корнях, в которые он отправляет действия. Единственная «логика», содержащаяся в саге, - это «логика процесса» (часто реализуемая как Statemachine), которая на основе полученных действий (а также последующих событий) определяет, что делать (т.е. какие действия отправлять)
  4. Saga-шаблоны реализуют своего рода шаблон распределенных транзакций. То есть: когда один из агрегатных корней (которые снова работают автономно, не зная о существовании друг друга), терпит неудачу, все действие, возможно, придется откатить.
  5. Это достигается за счет наличия всех совокупных корней, после завершения отчета о деятельности они возвращаются в Сагу. (В случае успеха, а также ошибки)
  6. В случае, если все совокупные корни возвращают успех, внутренняя машина состояний, если Saga определяет, что делать дальше (или решает, что сделано)
  7. В случае неудачи Сага выдает всем совокупным корням, которые принимали участие в последнем действии, так называемое Компенсационное действие, то есть: действие, чтобы отменить последнее действие, которое выполнил каждый из совокупных корней.
  8. Это может быть просто «минус 1 голос», если действие «плюс 1 голос», но это может быть более сложным, чем восстановление блога в предыдущей версии.
  9. Источник событий (см. Объединение этих двух статей в блоге) направлен на сохранение результатов всех действий, выполняемых каждым из агрегатных корней, в централизованном хранилище событий (в этом контексте изменения называются «событиями»).
  10. Это хранилище событий является «единой версией правды» и может использоваться для воспроизведения состояния всех объектов, просто повторяя сохраненные события (по сути, как журнал событий).
  11. Комбинация двух (то есть: предоставление агрегированным корням возможности использовать Event-Sourcing для передачи своих изменений перед передачей отчета в Saga) дает много хороших возможностей, одна из которых касается моего вопроса

Я почувствовал, что мне нужно снять это с плеча, так как это можно понять сразу. Учитывая этот контекст / мышление (опять же, пожалуйста, исправьте, если не так)

вопрос: когда агрегатный корень получает действие Compensate и если этот агрегатный корень передал на аутсорсинг свои изменения состояния с использованием Event-sourcing, не будет ли действие Compensate Action во всех ситуациях просто удалением последнего события в хранилище событий для этого данный совокупный корень? (Предполагая, что постоянная реализация позволяет удалять)

Это имело бы для меня большой смысл (и было бы еще одним большим преимуществом этой комбинации), но, как я уже сказал, я могу делать эти предположения на основе неправильного / неполного понимания этих концепций.

Я надеюсь, что это не слишком затянуто.

Благодарю.

Герт-Ян
источник

Ответы:

9

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

вспомните пример тележки от Грега Янга и убранных предметов. Может быть, завтра информация о компенсирующих действиях может иметь какую-либо ценность.

Что касается саги, то, насколько я знаю, все правильно. Вы должны думать о саге как о государственной машине. Это делает только это.

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

Это зависит от ваших бизнес-правил. Либо есть простое исправление: совокупный корень знает, что в таком случае вы делаете это, или выбор слишком сложен, чтобы делать его программно (стоимость разработки слишком высока), и, следовательно, вы можете предложить это какому-то пользователю, который может выбирать между различными действиями. Все зависит от контекста компенсирующего действия. Это чисто деловые правила. Что мы должны делать в том или ином случае. Это то, что нужно спросить у эксперта в своей области, а затем выбрать вместе с ним, будет ли лучше разработать автоматическое решение или решение состоит в том, чтобы спросить пользователя Рональда Р., потому что именно он обычно отвечает на этот вопрос.

Arthis
источник
Как агрегат узнает, в какое состояние следует вернуться? Будет ли разрешено делать запросы к хранилищу событий или к чему-то еще?
Герт-Ян
Это не должно возвращаться, но добавить другое событие. Это команда, которую выпускает сага, чтобы сказать агрегату что-то сделать. В этом действии будет событие.
Артис
Да, я понял. «Возврат» был, вероятно, плохо выбран. Примите во внимание следующее: я могу использовать Souras Event Sourcing для моделирования, среди прочего, процессов публикации / утверждения документов. Что, если редактор отправил действие ChangeBlogBody, которое в конечном итоге сохраняется как событие BlogBodyChanged, в хранилище событий. После этого по какой-то причине выдается Компенсационное действие. Как Агрегат узнает, какое событие компенсации запускается, что приводит к содержанию тела блога, как это было непосредственно перед выпуском действия ChangeBlogBody?
Герт-Янв
Это зависит от ваших бизнес-правил. Либо есть простое исправление: совокупный корень знает, что в таком случае вы делаете это, или выбор слишком сложен, чтобы делать его программно (стоимость разработки слишком высока), и, следовательно, вы можете предложить это какому-то пользователю, который может выбирать между различными действиями.
Артис
Все зависит от контекста компенсирующего действия. Это чисто деловые правила. Что мы должны делать в том или ином случае. Это то, что нужно спросить у эксперта в своей области, а затем выбрать вместе с ним, будет ли лучше разработать автоматическое решение или решение состоит в том, чтобы спросить пользователя Рональда Р., потому что именно он обычно отвечает на этот вопрос.
Артис
6

Для полноты я подумал включить соответствующий фрагмент от Мартина Фаулера о способах возврата состояния:

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

Сторнирование является наиболее простым, когда событие разыгрывается в форме разницы. Примером этого может быть «добавление $ 10 к учетной записи Мартина», а не «установка учетной записи Мартина на $ 110». В первом случае я могу вернуться, просто вычитая 10 долларов, но во втором случае у меня недостаточно информации, чтобы воссоздать прошлую стоимость аккаунта.

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

От: http://martinfowler.com/eaaDev/EventSourcing.html

Герт-Ян
источник
1

Концептуально:

  • Событие - это то, что случилось. Прошлое не может быть изменено.
  • Команда это то, что нужно сделать. Команда может не выполняться (может быть отклонена).

Мы можем изменить наше будущее состояние, только выполнив другую команду (Compensate Action), которая должна привести к тому, что события изменят состояние приложения.

Чтобы «сбить с толку» вопрос, подумайте над фразой «удалить последнее событие»:

  • Что, если последнее событие не является тем, которое должно быть удалено? Что если другие события произошли после удаляемого? Эти события также должны быть изменены (так как их базовое состояние изменилось бы путем удаления события перед ними).
  • Что если событие было отправлено во внешнюю систему? У вас может не быть доступа к исправлению его состояния, кроме отправки другого события.

Короче говоря, у вас нет возможности удалять события в шаблоне CQRS.

Вы можете уменьшить свой Event Store, создав состояние моментального снимка (который основан на всех событиях, ведущих к этому состоянию), но этот физический аспект не имеет ничего общего с концепцией события.

uvsmtid
источник
0

Герт-Ян, я тоже думаю, что действие компенсации может просто удалить соответствующее событие (я). Это имеет смысл и показывает еще одно преимущество шаблона проектирования Event Sourcing: более простую реализацию шаблона проектирования Compensating Transaction.

Некоторые говорят, что удаление события нарушает «принципы» поиска событий или CQRS. Я думаю, что это ограничивающее обобщение. Я думаю, что можно удалить событие, если оно было создано в рамках глобальной транзакции, которая отменяется. Рассмотрим псевдокод:

try {
    newEvents = processMycommand(myCommand)
    for (Event newEvent : newEvents)
        EventStore.insertEvent(newEvent);
} catch (Exception e) {
    EventStore.rollback();
}

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

Если это целесообразно для транзакции базы данных ACID (транзакция с фиксацией / откатом), почему она не подходит для компенсирующей транзакции?

При выполнении глобальной транзакции Saga изменения данных могут быть отменены (путем компенсации). Нет необходимости сохранять событие, созданное во время транзакции, поскольку транзакция не завершена.

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

Пауло Мерсон
источник
0

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

введите описание изображения здесь ссылка: https://cambridge-intelligence.com/bringing-time-series-data-to-life-with-keylines/

Ofc. для обычного сайта это не имеет значения, но эти решения предназначены для огромных веб-сайтов, таких как Facebook, Google и т. д. Так что на самом деле вопрос не имеет смысла, потому что, как бы вы удалили событие в базе данных только для добавления и как бы Вы называете компенсацию чем-то вроде создания машины времени и возврата во времени, чтобы изменить или предотвратить событие ???

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

inf3rno
источник