Як мені впоратися з побічними ефектами в Sourcing подій?


14

Припустимо, що ми хочемо реалізувати невелику підсистему безпеки для фінансової програми, яка попереджає користувачів електронною поштою, якщо виявлено дивний шаблон. У цьому прикладі схема буде складатися з трьох транзакцій, як зображено. Підсистема безпеки може читати події з основної системи з черги.

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

  1. Моніторинг активовано
  2. Операція оброблена
  3. Операція оброблена
  4. Операція оброблена
  5. Сповіщення спрацьовує (id: 123)
  6. Електронний лист для надісланого сповіщення (для id: 123)
  7. Операція оброблена

Маючи це на увазі, я вважав, що пошук подій може бути застосований тут дуже добре, хоча у мене є питання без чіткої відповіді. Попередження, викликане в прикладі, має чіткий побічний ефект, електронний лист потрібно надіслати, обставина, яка має відбутися лише один раз. Тому це не повинно відбуватися під час відтворення всіх подій сукупності.

Якоюсь мірою я бачу електронний лист, який потрібно надіслати аналогічно матеріалізаціям, породженим стороною запиту, яку я бачив стільки разів у літературі про джерела CQRS / події, хоча не настільки тонка різниця.

У цій літературі сторона запиту побудована з обробників подій, які можуть генерувати матеріалізацію стану в заданій точці, читаючи знову всі події. Однак у цьому випадку це неможливо зробити так, як це пояснювалося раніше. Ідея про те, що кожна держава минуща, тут не так добре застосовується . Нам потрібно зафіксувати той факт, що десь було надіслано попередження.

Простим рішенням для мене було б мати іншу таблицю або структуру, де ви ведете облік попереджуваних попереджень. Оскільки у нас є ідентифікатор, ми зможемо перевірити, чи було попередження з таким самим ідентифікатором подано раніше. Наявність цієї інформації зробить SendAlertCommand idempotent. Можна видавати кілька команд, але побічний ефект відбудеться лише один раз.

Навіть маючи на увазі це рішення, я не знаю, чи це натяк на те, що з цією архітектурою щось не так у цій проблемі.

  • Чи правильний мій підхід?
  • Чи є якесь місце, де я можу знайти більше інформації про це?

Дивно, що я не зміг знайти про це більше інформації. Можливо, я використав неправильне формулювання.

Дуже дякую!

Відповіді:


12

Як мені впоратися з побічними ефектами в Sourcing подій?

Коротка версія: модель домену не виконує побічних ефектів. Це відстежує їх. Побічні ефекти виконуються за допомогою порту, який підключається до кордону; коли електронний лист надіслано, ви відправляєте підтвердження назад до доменної моделі.

Це означає, що повідомлення електронної пошти надсилається поза транзакцією, яка оновлює потік подій.

Саме там, де знаходяться назовні - справа смаку.

Отже, концептуально у вас є потік подібних подій

EmailPrepared(id:123)
EmailPrepared(id:456)
EmailPrepared(id:789)
EmailDelivered(id:456)
EmailDelivered(id:789)

І з цього потоку можна створити складку

{
    deliveredMail : [ 456, 789 ],
    undeliveredMail : [123]
}

Складка повідомляє вам, які електронні листи не підтверджені, тому ви надсилаєте їх знову:

undeliveredMail.each ( mail -> {
    send(mail);
    dispatch( new EmailDelivered.from(mail) );
}     

Ефективно це двофазна фіксація: ви модифікуєте SMTP в реальному світі, а потім оновлюєте модель.

Наведена вище модель дає вам хоча б раз модель доставки. Якщо ви хочете максимум одного разу, можете перевернути його

undeliveredMail.each ( mail -> {
    commit( new EmailDelivered.from(mail) );
    send(mail);
}     

Існує бар'єр для транзакцій між тим, щоб зробити EmailPrepared довговічним і фактично надсилати повідомлення. Існує також бар'єр для транзакцій між відправленням електронної пошти та тим, щоб зробити EmailDelivered довговічним.

Надійне повідомлення Уді Дахана з розподіленими транзакціями може стати хорошою відправною точкою.


2

Вам потрібно відокремити "Події зміни держави" від "Дії"

Зміна стану подій є подія , яке змінює стан об'єкта. Це ті, які ви зберігаєте та перетворюєте.

Дія цього то об'єкт і до інших речей. Вони не зберігаються як частина пошуку подій.

Один із способів зробити це - з обробниками подій, які ви підключаєте чи не залежно від того, чи хочете Ви виконати дії.

public class Monitor
{
    public EventHander SoundAlarm;
    public void MonitorEvent(Event e)
    {
        this.eventcount ++;
        if(this.eventcount > 10)
        {
             this.state = "ALARM!";
             if(SoundAlarm != null) { SoundAlarm();}
        }
    }
}

Тепер у моїй службі моніторингу я можу мати

public void MonitorServer()
{
    var m = new Monitor(events); //11 events
    //alarm has not been sounded because the event handler wasn't wired up
    //but the internal state is correctly set to "ALARM!"
    m.SoundAlarm += this.SendAlarmEmail;
    m.MonitorEvent(e); //email is sent
}

Якщо вам потрібно зареєструвати надіслані електронні листи, ви можете це зробити як частина SendAlarmEmail. Але вони не є подіями в значенні Event Sourcing

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.