Event-Sourcing как изменить бизнес-правила - PullRequest
0 голосов
/ 30 мая 2018

Мое приложение использует cqrs и источник событий.Это уже в производстве.Теперь я должен добавить бизнес-правила.Мои бизнес-правила находятся в моем общем корне UserAggregate.

Мои команды:

public class CallUserForMarketingPlanCommand 
{
    public Guid UserId {get;set;}
    public DateTime CallDate {get;set;} 
    public Guid PlanId {get;set;}
}

public class AcceptMarketingPlanCommand
{
    public Guid UserId {get;set;}
    public Date AswerDate {get;set;}
    public Guid PlanId {get;set;}
}

... the same thing for RefuseMarketingPlanCommand

эти команды применяются к моему сводному корню, который генерирует события, хранящиеся в хранилище событий

Теперь, если через 50 дней после вызова пользователь выполнитне дать ответ, пользователь должен быть отозван оператором.Чтобы сделать это, я думаю, сгенерировать событие UserDoNotRepliedInDelayEvent и использовать его для проецирования на модель чтения с информацией отзыва.

Мое решение - создать отложенную команду (из обработчика UserCalledForMarketingPlanEvent) CheckUserAnswerCommand, которая проверяет дату вызова и генерирует UserDoNotRepliedInDeпри необходимости через совокупность.Хорошо.

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

РЕДАКТИРОВАТЬ:

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

Отключить учетную запись, если два платежа не разрешены.

Это правило поставляется с первым развертыванием.Хорошо, теперь отключено 1000 учетных записей.Босс меняет правило, потому что это влияет на бизнес, и хочет отключить учетную запись, если не выполнено 5 платежей.

Как включить учетную запись, у которой не выполнено менее 5 платежей?

Спасибо за вашу помощь.

Ответы [ 2 ]

0 голосов
/ 13 июня 2018

Вы также можете использовать Saga, которая отслеживает процесс и затем создает команду типа «neneeded », когда время истекло.он также отслеживает события, которые говорят Сага о завершении, если в течение 50 дней был звонок.(Имейте в виду, что сага является частью логики вашего домена и действует как AR, если вы выполняете DDD)

0 голосов
/ 02 июня 2018

Теперь, если через 50 дней после звонка пользователь не даст ответ, пользователь должен быть отозван оператором.Чтобы сделать это, я думаю, сгенерируйте событие UserDoNotRepliedInDelayEvent и используйте его для проецирования на модель чтения с информацией об отзыве.

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

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

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

С этого момента это зависит от вашей архитектуры.

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

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

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

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

Отключите учетную запись, если два платежа не разрешены.

Это правило относится к первому развертыванию.Хорошо, теперь отключено 1000 учетных записей.Начальник меняет правило, потому что это влияет на бизнес, и хочет отключить учетную запись, если не выполнено 5 платежей.

Как включить учетную запись, у которой не выполнено менее 5 платежей?

Эточасть вашего вопроса более запутанная.Из того, что я понял, у вас когда-то было правило, которое гласило: «Счета с двумя или более просроченными платежами должны быть деактивированы», и вы хотите изменить это правило на «Счета с пятью или более просроченными платежами должны быть деактивированы».Если это так, то вам приходится иметь дело с этим на нескольких уровнях ...

  • Сначала вы должны сначала внедрить новое правило в вашей модели команд, так же, как это всегда былоно с обновленным параметром.

  • Во-вторых, вы не можете ретроактивизировать аккаунты с 2,3,4 просроченными платежами, игнорируя их «события деактивации».С вашей точки зрения хранилище событий это произошло, и вы должны соблюдать правила, согласно которым хранилище событий является хранилищем «только для отправки» .Таким образом, вы должны использовать компенсационные события для их повторной активации после изменения правила.

Итак, если вы позаботились о первой теме (и ваш домен запущен с новым правилом)и поскольку вы не можете использовать ярлык из-за второй темы, один из ваших более простых вариантов - просто разработать однократную операцию, которая найдет учетные записи с 2,3,4 просроченными платежами, которые в настоящее время отключены, и добавит их к своему событию.хранит событие реактивации.На этом этапе вам придется заново создавать любые уязвимые модели чтения, если ваша архитектура не делает этого автоматически.

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

С точки зрения хранилища событий ...каждая из этих учетных записей будет иметь что-то подобное в своих потоках событий:

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

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

РЕДАКТИРОВАТЬ:

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

Если это так, то ответ будет более сфокусирован на строках "не должно быть задним числом действий"в домене событий ".

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

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

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

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

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

  • Если ваша архитектура объединяет различные контексты / домены / микросервисы, это также может потребовать дальнейшей оценки.Скажем, контекст-А выпустил трансграничное сообщение для контекста-Б из-за заданного состояния объекта.Продвигаясь вперед, вы изменяете состояние объекта, вмешиваясь в поток событий.Теперь существует вероятность того, что эти контексты могут остаться несовместимыми между собой, так как контекст B считает, что у объекта было состояние, которого у него больше нет.Это может быть очень уместно в вашем сценарии.

...