Event Sourcing - «Обновление» проекторов - PullRequest
0 голосов
/ 19 сентября 2018

Я недавно начал работать над проектом, который использует CQRS / ES в качестве базовой архитектуры, но я наткнулся на небольшую загадку и не смог найти какой-либо достоверной информации о том, как решить эту конкретную проблему.

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

Очевидно, что это потребует повторной обработки всего потока событий (или с некоторой контрольной точки), но с учетом дополнительных событий в новой проекции.

Мой вопрос: как люди обычно решают эту проблему?Из того, что я понимаю, это был бы случай запуска новой проекции рядом с текущей проекцией, а затем снятия старой проекции вместо новой.

Хотя это вызывает несколько дополнительных вопросов:

  1. Как можно изящно справиться с этим обновлением, если я проецирую на реляционную модель, как бы мне было сделать так, чтобы переключение было как можно более плавным, учитывая, что в обоих проекциях будут использоваться разныетаблицы (возможно, даже в разных базах данных).
  2. Может ли старая проекция полностью утилизироваться на этом этапе, или было бы разумно оставить конфигурацию старой проекции где-нибудь в памяти?(Однако я не могу придумать причину, по которой вам нужно создать более старую проекцию)

Любые указатели были бы хороши:)

Ответы [ 3 ]

0 голосов
/ 21 сентября 2018

Учитывая, что все события в вашем хранилище событий последовательно упорядочены.

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

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

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

0 голосов
/ 02 декабря 2018

У вас две проблемы в одной.Давайте разделим его на две отдельные вещи:

  • О реляционной модели, куда писать.
  • О отбрасывании старых проекций.

Первое, что выНеобходимо проанализировать, контролируете ли вы свои читатели или нет, и если они развернуты мономолитически со всем проектом или нет.

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

Предположим, вы находитесь в сложной ситуации.

a.Проектирование новых таблиц

Проектирование новых таблиц для нового проектора, например, если ваши таблицы были Monitors, Mouses, Keyboards и AudioDevices, вы можете теперь захотеть создать Monitors_v2,Mouses_v2, Keyboards_v2 и Audiodevices_v2 или любое другое значимое имя / чистое кодовое имя, которое оно представляет для вашей компании (например, MonitorsWithManufacturerInformation)

Если ваши мониторы изменились, а ваши клавиатуры - нет, яСоветую также составить полностью отдельный набор таблиц всех агрегатов и связанных с ними концепций, чтобы избежать какого-либо взаимодействия со старым.Если у вас есть Keyboards_v2 и Keyboard с дублированными данными, никто не будет жаловаться.Если вы заставите их быть в одной таблице, вы будете убиты взаимодействиями.

b.Кодирование новых проекторов

У меня обычно внутри проектора есть возможность оставлять и создавать свои собственные таблицы или таблицы.Например, все мои проекторы реализуют метод resetState(), принудительно вызванный интерфейсом Projector, который, как правило, просто отбрасывает и создает несколько таблиц:

public function resetState()
{
    $this->dropAndCreateTables();
    $this->resetLastProcessedEventPointer();
}

private function dropAndCreateTables()
{
    $this->dropAndCreateTableFormSubmissions();
    $this->dropAndCreateTableDashboard();
    $this->dropAndCreateTableStateTypes();
}

Затем добавьте возможность Projecto для обработки:

  • отдельное событие
  • , а также все ожидающие события

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

c.Разверните и запустите

Разверните новый код (для предварительной подготовки, если он у вас есть) и выполните следующее:

  • состояние сброса
  • обработка всех событий

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

d.Повторите кодирование и развертывание

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

Повторно осматривайте и так далее, пока не получите удовлетворительный результат.

e.Подключитесь к живому потоку

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

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

ф.Общайтесь с товарищами по команде, заставляя читателей

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

г.Обратная совместимость: да или нет?

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

Если вы сомневаетесь (т.е. уверены, что 99.999999999%), просто не удаляйте старые версии.

Если вы уверены (то есть: уверены на 100,000000%) отключите проекторы для некоторых циклов продукта (это будут минуты, дни или недели в зависимости от вашего проекта) и убедитесь, что никто не жалуется на чтение испорченных данных (повторная проверка).

Если кто-то жалуется: очень легко запустить старые проекторы, чтобы узнать последние события и подключить их заново.Дождитесь, пока читатель скажет «Я обновился до v2», и повторите.

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

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

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

Итак, резюмируем: краткие ответы

Ваш вопрос № 1: Как это возможно?чтобы корректно справиться с этим обновлением, если я проецирую на реляционную модель, как бы мне было сделать так, чтобы переключение было максимально плавным, учитывая, что в обоих проекциях будут использоваться разные таблицы (возможно, даже в разных базах данных).

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

У вас вопрос № 2: можно ли полностью избавиться от старой проекции на этом этапе или было бы разумно сохранить конфигурациюстарая проекция где-то валяется в хранилище?(Однако я не могу придумать причину, по которой вам нужно было бы создать более старую проекцию)

  • Вы не можете удалить ни данные, ни код, если вы не уверены на 100%, что читатели не торчат.Когда читателей нет, вы можете отключить их на некоторое время, чтобы повысить осведомленность в последнюю минуту (существует ли это слово?).После безопасного времени без жалоб вы можете безопасно удалить данные без необходимости резервного копирования, так как фактически они были «функцией» событий.Вы также можете безопасно удалить код старого проектора, если для вашего кода используется система контроля версий.

Надеюсь на помощь!

0 голосов
/ 19 сентября 2018

Грэг Янг советует рассматривать вашу проекционную функцию как неизменную после производства.

То есть, если вам нужна другая модель чтения, вы просто создаете новую (с нулевого времени), а когда вам не нужна старая - вы удаляете ее.

Да,это может занять время и дополнительное хранилище, но в наши дни это не такая большая проблема.

...