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


10

Кажуть, що в CQRS виправити помилку легко, ви просто перерозподіліть і потім повторно відтворюєте події.

Але що робити, якщо одна з подій повинна призвести до того, що зовнішня система, яка не перебуває у вашому контролі, "відправить товар" замовнику, якщо ви просто повторите події, предмет буде доставлений двічі.

Як ви вирішуєте це?

Відповіді:


6

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

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


"Не забувайте, що крок повторного використання означає фактично повернути стан зчитуваної моделі до більш раннього часу. Це, мабуть, нічого, що ви не можете зробити (або потрібно зробити) для стану зовнішніх систем." -> але що робити, якщо я хочу, щоб повторна спроба повторила невдалі зовнішні виклики системи, як-от доставка? У цьому випадку моя повторна розробка повтору не тільки скине стан зчитуваної моделі, але й спричинить зовнішні події. Це звучить справедливо чи я щось пропускаю?
Жас

2
@Jas: ви не хочете зловживати "повтором", щоб повторити невдалий зовнішній виклик системи. Ви використовуєте "повтор" для отримання моделі зчитування власної системи у тому ж стані, що був раніше. Це означає, що у випадку невдалого запиту на доставку ваша система була поінформована раніше про цю помилку та зберігала цю інформацію десь у своєму стані. Повтор переконує, що ця інформація все ще залишається після "повторного розміщення та повторного відтворення". Тож після повторного відтворення ваша система може застосувати стратегію "повторна доставка в разі відмови" (яка не має нічого спільного з CQRS; будь-яка надійна система замовлення просто повинна мати таку стратегію).
Док Браун

Цікаво, це те, що я мав на увазі зробити, просто цікавилось, чи є в цьому «візерунок», щоб я не винаходив колесо!
Яс

3

Від Мартіна Фаулера сорсингу події статті:

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

Отже, коли вам потрібно відновити стан вашої системи до певного моменту, ви переймете збережений стан , а не обробники подій, до цього моменту.

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


2

Але що робити, якщо одна з подій повинна призвести до того, що зовнішня система, яка не перебуває у вашому контролі, "відправить товар" замовнику, якщо ви просто повторите події, предмет буде доставлений двічі.

Щоб вибрати конкретний приклад, давайте розглянемо, як може «принаймні один раз» підійти до побічних ефектів.

State currentState = State.InitialState
for(Event e : events) {
    currentState = currentState.apply(e)
}
for(SideEffect s : currentState.querySideEffects()) {
    performSideEffect(s)

Отже модель домену відстежує, що потрібно зробити; але фактичне виконання залишає у додатку

У контексті виконання команди основна ідея виглядає однаково. Фактичні побічні ефекти трапляються поза транзакцією, яка оновлює модель.

Тож одиничні тести для вашої моделі можуть виглядати приблизно

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced)

    // Then
    List.of(SendEmail) === currentState.applyAll(events).querySideEffects()
}

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced, EmailSent)

    // Then
    List.EMPTY === currentState.applyAll(events).querySideEffects()
}

Тут є основні моменти

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