Логическое исключение в глючном несогласованном потоке событий в DDD, CQRS, EventSourcing? - PullRequest
0 голосов
/ 31 августа 2018

Допустим, вы подходите к DDD с помощью EventSourcing.

Мы все знаем, что события неизменны, и их никогда не следует удалять из журнала событий. Но что, если поток логически «неверен»? Не тот классический случай, когда «я добавил деньги, мне не нужно было добавлять их, поэтому создайте компенсационное событие, чтобы вывести их».

Я не говорю о исключениях времени выполнения , но логических исключениях , которые вы можете найти в потоке событий, потому что кодеры допустили ошибки в средствах записи событий.

Вопрос

Как вы «воспроизводите» поток событий, если в программном обеспечении, которое его записало, были ошибки, нарушавшие логику домена?

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

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

И неожиданно, когда воспроизводит поток событий, вы обнаруживаете "несвязные" события, которые не соответствуют ни текущим бизнес-правилам, ни тем правилам, которые существовали тогда.

Пример 1

У вас есть следующие события:

#  TimeStamp  Event          Data
------------------------------------------------------
1  03/jul     car.created    { id: 4444, color: blue }
2  14/jul     car.delivered  { id: 4444, to: Alice }
3  18/jul     car.created    { id: 5555, color: blue }
4  22/jul     car.created    { id: 5566, color: orange }
5  25/jul     car.created    { id: 5577, color: blue }

26 июля кто-то спрашивает: «Сколько у вас синих автомобилей в наличии?».

Кристально чистый: 2 единицы (идентификаторы 5555 и 5577).

Причина: Единица 4444 была продана. Единица 5566 оранжевого цвета.

Но что, если у вас есть эта глючная последовательность?

#  TimeStamp  Event          Data
------------------------------------------------------
1  03/jul     car.created    { id: 4444, color: blue }
2  14/jul     car.delivered  { id: 4444, to: Alice }
3  18/jul     car.created    { id: 5555, color: blue }
4  22/jul     car.created    { id: 5566, color: orange }
5  23/jul     car.created    { id: 5555, color: red }
6  25/jul     car.created    { id: 5577, color: blue }

Конечно, событие 5 никогда не должно было произойти, вы не можете создать один и тот же юнит 2 раза.

После исследования экспертов домена ... вы обнаружите, что событие 5 неверно. Должно быть написано «car.repainted», но программное обеспечение содержит ошибки и написано «car.created».

Вопрос к примеру 1:

  • Вы бы добавили новые события с номерами 7 и более с меткой времени «сразу после» события 5, чтобы сделать какую-то компенсацию? Какие события вы бы написали?
  • Вы бы добавили новые события с номерами 7 и более, с отметкой времени «непосредственно перед» событием 5, чтобы сделать своего рода сигнал для проигрывателя «эй, игнорируйте следующее создание»? Какие события вы бы написали?
  • Не могли бы вы переписать ваши "проигрыватели", чтобы они могли интерпретировать, что "все до 25 / июл", что означает "двойное создание", означает "car.repainted" и перезапускать проигрыватели, чтобы восстановить агрегаты?
  • Будете ли вы нарушать золотые правила и «трогать» историю? На самом деле это не «история», потому что событие «5» на самом деле не произошло. Можем ли мы потрогать его тогда?

Пример 2

Давайте предположим, что склад с вилочным погрузчиком забирает вещи с полок. Склад содержит 2 вертикальных коридора, 2 горизонтальных коридора и 1 диагональный коридор.

Все коридоры являются двунаправленными, за исключением левого вертикального, который имеет какие-то ступеньки или что-то еще, и вилочный погрузчик может двигаться только от А к С, но не наоборот; кроме того, за исключением горизонтальной плоскости, которая также имеет ступени, и погрузчик может двигаться только от D к C и никогда от C к D.

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

Команды могут быть:

purchase()
start()
goRight()
goLeft()
goUp()
goDown()
cross()

События могут быть:

purchased
started
wentRight
wentLeft
wentUp
wentDown
crossed

Это возможная диаграмма состояния агрегата вилочного погрузчика:

eventsourcing aggregate state diagram

Предположим, что вы воспроизводите события совокупности, и вы нашли их:

#   TimeStamp     Event
----------------------------------------------
1   12/jul 10:00  purchased
2   14/jul 09:00  started
3   14/jul 11:00  wentDown
4   14/jul 12:00  crossed
5   14/jul 14:00  wentDown
6   23/jul 09:00  started
7   23/jul 10:00  wentRight
8   23/jul 13:00  crossed

Кто-то спрашивает: «Где сейчас грузоподъемник? Вы можете легко сказать« С ».

Причина: независимо от того, что произошло до 6, потому что событие 6 сбрасывается в положение A, событие 7 движется в направлении B, событие 8 движется в направлении C.

Но что, если у вас последовательность продолжается вот так?

#   TimeStamp     Event
----------------------------------------------
[...]
6   23/jul 09:00  started
7   23/jul 10:00  wentRight
8   23/jul 13:00  crossed
9   23/jul 15:00  wentUp
10  23/jul 16:00  wentRight
11  27/jul 09:00  started
12  27/jul 11:00  wentDown

Какой-то эксперт в области спрашивает вас: «Привет, парень, ты сказал нам, что аутсорсинг был волшебным: где был вилочный погрузчик 23 июля в 18:00?»

Мы все знаем, что лифт не может «перепрыгнуть» через лестницу, поэтому мы все знаем, что событие 9 никогда не может произойти.

Так что наши «переигровщики» не могут делать ничего другого, что вызывает исключение. Но уже записана последовательность событий.

Тема здесь не «как написать хорошую последовательность», а «что делать, когда вы сталкиваетесь с последовательностью с исключениями».

Вопросы для примера 2:

  • Вы бы написали компенсирующее событие? Как? Который? Когда?
  • Переписали бы историю? (некрасиво, если у вас миллионы событий)
  • Как бы вы обработали это исключение с точки зрения доменного события replayers ?

1 Ответ

0 голосов
/ 31 августа 2018

Как вы «воспроизводите» поток событий, если в программном обеспечении, в котором он был записан, содержались ошибки, нарушавшие логику домена?

У вас есть как минимум два варианта:

  1. Поместите некоторый лечебный код в метод Aggregate apply или в подписчики события (Readmodels, projection, Sagas); этот фрагмент кода должен обрабатывать ту ситуацию, которую вы пытаетесь избежать.

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

  1. Перенос магазина событий. У Грега Янга есть книга о том, как это сделать. По сути, вы создаете другой поток событий на возможном другом экземпляре хранилища событий, обрабатываете каждое событие из потока событий, исправляете аномалию и добавляете в новый поток событий. После завершения миграции вы заменяете старое хранилище событий новым хранилищем событий.

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

Вы бы написали компенсирующее событие? Как? Который? Когда?

Запись событий компенсации удобна, когда вам нужно быстрое решение; это частный случай решения нет. 1.

Вы бы нарушили золотые правила и «прикоснулись» к истории? На самом деле это не «история», потому что событие «5» на самом деле не произошло. Можем ли мы потрогать его тогда?

Вы можете сделать это, я, конечно, сделал, потому что я хотел самое быстрое решение, которое возможно, но оно может стать уродливым в зависимости от фреймворка / технологии. Например, подписчики больше не могут быть уверены, что они обработали все соответствующие события из хранилища событий, поэтому, чтобы быть уверенным, вам необходимо перестроить все Readmodels; самые большие проблемы у вас с Sagas, потому что обработка событий имеет побочные эффекты.

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

...