Обробка одночасності ES / CQRS


20

Нещодавно я почав занурюватися в CQRS / ES, тому що мені може знадобитися застосувати це на роботі. Це здається дуже перспективним у нашому випадку, оскільки це вирішило б багато проблем.

Я накреслив своє грубе розуміння того, як додаток ES / CQRS повинен виглядати контекстуально під спрощений випадок використання банківських операцій (зняття грошей).

ES / CQRS

Просто підводячи підсумки, якщо людина A знімає гроші:

  • команда видається
  • команда передається для перевірки / перевірки
  • подія висувається в магазин подій, якщо перевірка успішна
  • агрегатор відмовляється від події застосувати зміни до сукупності

З того, що я зрозумів, журнал подій є джерелом істини, оскільки це журнал ФАКТІВ, ми можемо вивести з нього будь-яку проекцію.


Тепер, що я не розумію, в цій грандіозній схемі речей, що відбувається в цьому випадку:

  • правило: баланс не може бути негативним
  • людина A має баланс 100e
  • особа A видає ВиведенняCommand 100e
  • перевірка валідації та випромінюється подія MoneyWithdrewEvent of 100e
  • тим часом, людина A видає ще один WithdrawCommand 100e
  • перший MoneyWithdrewEvent ще не зведений, тому перевірка проходить, тому що перевірка перевірки сукупності (що ще не оновлено)
  • MoneyWithdrewEvent з 100e випромінюється в інший раз

==> Ми знаходимось у несуперечливому стані балансу на рівні -100e, а журнал містить 2 MoneyWithdrewEvent

Як я розумію, існує декілька стратегій для вирішення цієї проблеми:

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

Питання, що стосуються стратегій:

  • а) У цьому випадку журнал подій вже не є джерелом істини, як з цим боротися? Крім того, ми повернулися до клієнта ОК, тоді як дозволити відкликання було абсолютно неправильно, чи краще в цьому випадку користуватися замками?
  • б) Замки == тупики, чи маєте ви уявлення про кращі практики?

Загалом, чи правильно я розумію, як поводитися з одночасністю?

Зауважте: я розумію, що та сама людина вилучає двічі гроші за такий короткий проміжок часу, що неможливо, але я взяла простий приклад, щоб не загубитися в деталі


Чому б не оновити сукупність на кроці 4, а не чекати до кроку 7?
Ерік Ейдт

Отже, ви маєте на увазі, що в цьому випадку магазин подій - це лише журнал, який читається лише після запуску програми для відтворення агрегатів / інших проекцій?
Луї Ф.

Відповіді:


19

Я накреслив своє грубе розуміння того, як додаток ES / CQRS повинен виглядати контекстуально під спрощений випадок використання банківських операцій (зняття грошей).

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

Кожен раз, коли команда обробляється або повторюється (ви зрозумієте, будьте терплячі) виконуються наступні дії:

  1. команда досягає обробника команд, тобто служби в Application layer.
  2. обробник команд ідентифікує Aggregateі завантажує його з репозиторію (у цьому випадку завантаження виконується шляхом new-ing Aggregateекземпляра, отримання всіх раніше випромінюваних подій цього агрегату та повторного застосування їх до самого агрегату; версія агрегату зберігається для пізніше використання; після застосування подій сукупність знаходиться в остаточному стані - тобто залишок поточного рахунку обчислюється як число)
  3. обробник команд викликає відповідний метод на Aggregate, як, Account::withdrawMoney(100)і збирає вихідні події, тобто MoneyWithdrewEvent(AccountId, 100); якщо на рахунку не вистачає грошей (залишок <100), тоді підвищується Виняток і все припиняється; в іншому випадку виконується наступний крок.
  4. обробник команд намагається зберігати Aggregateдо сховища (у цьому випадку сховище - це Event Store); це роблять, додаючи нові події до Event streamif і тільки якщо the versionof Aggregateвсе ще є тим, що було коли Aggregateбуло завантажено. Якщо версія неоднакова , то команда повторюється - перейдіть до кроку 1 . Якщо значення versionзбігається, події додаються до Event streamі клієнту надається Successстатус.

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

Термін Event stream- це абстракція навколо всіх подій, які випромінював той самий агрегат.

Ви повинні розуміти, що Event storeце просто інший вид наполегливості, де зберігаються всі зміни сукупності, а не лише остаточний стан.

а) У цьому випадку журнал подій вже не є джерелом істини, як з цим боротися? Крім того, ми повернулися до клієнта ОК, тоді як дозволити відкликання було абсолютно неправильно, чи краще в цьому випадку користуватися замками?

Магазин подій завжди є джерелом істини.

б) Замки == тупики, чи маєте ви уявлення про кращі практики?

За допомогою оптимістичного блокування у вас немає блокувань, просто повторіть команду.

У будь-якому разі, Замки! = Тупики


2
Існує певна оптимізація щодо завантаження місця, Aggregateде ви не застосовуєте всі події, але ви зберігаєте короткий огляд Aggregateдо певної точки в минулому і застосовуєте лише події, що відбулися після цього пункту.
Костянтин Гальбену

Гаразд, я думаю, що моя плутанина випливає з того, що магазин подій == шина подій (я маю на увазі кафку), тому перестроювання сукупності може бути дорогим, оскільки вам може знадобитися перечитати багато подій. Якщо у вас є знімок Aggregate, коли слід оновлювати знімок? Чи сховище знімків схоже на магазин подій чи це матеріалізований вигляд, отриманий з шини подій?
Луї Ф.

Існує кілька стратегій щодо створення знімка. Одне - робити знімки кожні n подій. Ви повинні зберігати знімок разом з подіями, в тому самому місці / постійності / базі даних, на одній і тій же комісії. Ідея полягає в тому, що знімок сильно пов'язаний з версією агрегату.
Костянтин Гальбену

Гаразд, я думаю, я маю чіткіше бачення, як з цим впоратися. Тепер останнє запитання, яка роль в кінцевому підсумку? Якщо сукупності оновлюються синхронно?
Луї Ф.

1
Так, ви можете використовувати RabbitMQ або будь-який канал, який ви хочете, щоб асинхронно надсилати події до прочитаних моделей, але лише після того, як ви збережете їх у сховищі. Дійсно, перевірка подій не проводиться після їх збереження: події представляють факти, що відбулися; прочитана модель може або не сподобається, що щось сталося, але це не може змінити історію.
Костянтин Гальбену

1

Я накреслив своє грубе розуміння того, як додаток ES / CQRS повинен виглядати контекстуально під спрощений випадок використання банківських операцій (зняття грошей).

Закрити. Проблема полягає в тому, що логіка оновлення вашого "сукупності" знаходиться в дивному місці.

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

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

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

Іншими словами, стрілки 2 і 3 (якщо вони є) зазвичай були б з'єднані з магазином подій, а не з сукупним сховищем.

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

Варіації цього є звичайним випадком - замість того, щоб додавати до потоку в потоці подій, ми зазвичай PUT до певного місця в потоці; якщо ця операція несумісна зі станом магазину, запис припиняється, і сервіс може обрати відповідний режим відмови (відмовитись від клієнта, повторити, об'єднати ....). Використання idempotent write вирішує низку проблем при розподілі повідомлень, але, звичайно, потрібно мати магазин, який підтримує idempotent write.


Хм, я думаю, що я неправильно зрозумів компонент магазину подій. Я думав, що все має пройти через це і стає потоковим. Що робити, якщо мій магазин подій є кафкою і читається лише? Я не можу дозволити собі на кроках 2 і 3 повторно проглянути всі повідомлення. Здається, загальне моє бачення відповідало цьому: medium.com/technology-learning/…
Луї Ф.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.