Чому команди та події представлені окремо?


78

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


Це сильно залежить від того, що ви точно мали на увазі, коли вживаєте слова "команда" та "подія".
Doc Brown

Візьмемо для прикладу типовий проект CQRS / DDD.
alphadogg

4
Я розумію: команди повинні оброблятися рівно одним одержувачем (який може відхилити команду), а події можуть оброблятися 0 ... n приймачами.
Леві Фуллер,

Відповіді:


157

Команди можуть бути відхилені.

Події трапились.

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

Тепер, так як команди є те , що ми хочемо , щоб це відбулося, і події є те , що вже відбулося, ми повинні використовувати різні дієслова , коли ми називаємо ці речі. Це приводить до окремих подань.

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

Це ще одна причина, чому вони представлені окремо. Концептуальна ясність.

Команди та Події - це і Повідомлення. Але насправді вони є окремими поняттями, і їх слід чітко моделювати.


Гаразд, чи є між ними практичні відмінності на рівні впровадження? Наприклад, інший інтерфейс?
alphadogg

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

8
З командами ви використовуєте слово "Надіслати" (ви дбаєте про ціль цієї операції), тоді як з подіями ви використовуєте "Опублікувати" (вам байдуже, хто з іншого боку).
Ів Рейнхаут

4
@ quentin-starin Я знаю, що це вже давно, але я хотів би, щоб я проголосував за вашу відповідь, як 10 разів ... Я збирався піти шляхом "чому все не є подією, деякі з них просто реагують" - напр., "Щось просилося", а потім "Щось сталося"
Майкл Вассер

1
Розглянемо сутність, яка зберігається в mysql, і викликає подію entityIndexed. Ця ж подія прослуховується службою індексу, яка при отриманні події отримує сутність і індексує її в еластичному пошуку. Ви все-таки назвали б це подією чи командою з точки зору служби індексів?
Техношафт"

8

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

Скажімо, наприклад, що після створення Клієнта Ви також хочете ініціалізувати деякі значення облікових записів і т. Д. Після того, як АР Клієнта додає подію до EventDispatcher, і це отримує об'єкт CustomerCreatedEventHandler, цей обробник може викликати відправлення команди, яка виконає все, що вам потрібно, тощо.

Також існують DomainEvents та ApplicationEvents. Різниця просто концептуальна. Ви хочете спочатку відправити всі події вашого домену (деякі з них можуть спричинити події додатків). Що я маю на увазі під цим?

Ініціалізація облікового запису після того, як відбувся CustomerCreatedEvent - це подія DOMAIN. Надсилання сповіщення електронною поштою Клієнтові є Подією заявки.

Причина, по якій ви не повинні їх змішувати, зрозуміла. Якщо ваш SMTP-сервер тимчасово не працює, це не означає, що це має вплинути на вашу ДОМЕННУ РОБОТУ. Ви все ще хочете зберегти не пошкоджений стан своїх агрегатів.

Зазвичай я додаю події до свого диспетчера на рівні сукупного кореня. Ці події - або DomainEvents, або ApplicationEvents. Може бути і того, і іншого може бути багато. Після завершення мого обробника команд і повернення до стеку коду, який виконує обробник команд, я перевіряю свій диспетчер та відправляю будь-який інший DomainEvent. Якщо все це вдало, я закриваю транзакцію.

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

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

Тоді у вас є Саги .... але це ВІДМОВО ВИМИКАТЬ сферу цього питання :)

Це має сенс?


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

Отже, коли я прочитав власну відповідь, я бачу плутанину. Події насправді можуть бути одними. Події. Тоді у вас є обробники, які можуть мати таку відмінність. У вас може бути два обробники для однієї події, тоді ви можете запускати їх у різному контексті. Ви можете вирішити активувати "Обробники подій домену" в рамках активної транзакції, а потім, після цього, можна активувати "Обробники подій додатків". Я просто не бачу сенсу надсилати повідомлення електронної пошти чи смс в рамках Транзакції. Можливо, ви можете додати "завдання" до бази даних і виконати інший процес їх виконання, наприклад, надсилання електронних листів. Це має сенс?
Пепіто Фернандес,

6

Подія є фактом з минулого.

Команда тільки запит, і , таким чином , може бути відмовлено.

Важливою характеристикою команди є те, що вона повинна бути оброблена лише один раз одним одержувачем. Це пояснюється тим, що команда - це одна дія або транзакція, яку ви хочете виконати у програмі. Наприклад, одна і та ж команда створення замовлення не повинна оброблятися більше одного разу. Це важлива різниця між командами та подіями. Події можуть оброблятися кілька разів, оскільки багато систем або мікросервісів можуть бути зацікавлені у події. 'msdn'


5

Опрацювавши кілька прикладів, особливо презентацію Грега Янга ( http://www.youtube.com/watch?v=JHGkaShoyNs ), я дійшов висновку, що команди є зайвими. Це просто події від вашого користувача, вони натиснули цю кнопку. Ви повинні зберігати їх точно так само, як і інші події, оскільки це дані, і ви не знаєте, чи хочете ви використовувати їх у майбутньому поданні. Ваш користувач дійсно додав, а потім пізніше вилучив цей предмет з кошика або принаймні спробував. Можливо, згодом ви захочете використовувати цю інформацію, щоб нагадати користувачеві про це пізніше.


Описаним @ quentin-starin способом, розглядаючи подію як щось, що сталося, а команду як щось, що ми хочемо здійснити (запит), не зупиняє запис подій при натисканні кнопки, просто ці події не t обов’язково має призвести до команди або результату до команди, за якою діяли.
фрактор

Я все ще вважаю, що команди зайві. Я просто називаю те, що я роблю, функціональним джерелом подій. Нещодавній мій блог із ES та F # Elm як цілісною
Ant

2
Команди відокремлюють локальні події від віддалених дій. У вашому прикладі споживач події UserPressButton не буде реагувати на UserSelectedMenu або ScriptDidSomething, якщо він також не знає про ці речі. Крім того, команди, як правило, спрямовані на конкретного споживача; знову ж таки, у вашому прикладі споживач події UserPressButton не може визначити, чи встановлено для користувача прапорець "Підтвердити" чи ні, якщо ми не додамо ще більше зв'язку. За допомогою команд вживана дія може залежати від стану відправника або навіть від зовнішньої політики. Події самі по собі роблять це майже неможливим.
Доктор Еваль,

Перевірте цей проект - github.com/gregoryyoung/mr . Він використовує як команди, так і події.
xhafan

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

2

Вони представлені окремо, оскільки вони представляють дуже різні речі. Як сказав @qstarin, команди - це повідомлення, які можна відхилити, і що при успіху призведе до події. Команди та події є Dtos, це повідомлення, і вони, як правило, виглядають дуже схожими при створенні та сутності, проте з цього моменту, не обов'язково.

Якщо ви стурбовані повторним використанням, тоді ви можете використовувати команди та події як конверти для вашого (messge) корисного навантаження

class CreateSomethingCommand
{
    public int CommandId {get; set;}

    public SomethingEnvelope {get; set;}
 }

однак, мені хотілося б знати, чому ви запитуєте: D тобто у вас занадто багато команд / подій?


1
Ні, у мене їх не надто багато, оскільки я прагну побудувати свою першу таку систему! :) Я перебуваю в режимі навчання. Я намагаюся зрозуміти, чи CommandHandlers та EventHandlers роблять щось інше, або в основному мають однаковий інтерфейс.
alphadogg

Цікавим моментом є те, що команди та події можуть бути різними, наприклад, скажімо, у вас є CheckoutCartCommand, подія, ймовірно, матиме набагато більше даних, ніж команда, може бути також багато команд. Дуже рекомендуємо вам заглянути на github.com/MarkNijhof/Fohjin та github.com/gregoryyoung/mr
кругла криза

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

@Yves: ну враховуючи те, що конверт Something містить інформацію, необхідну для команди (якби це було створення клієнтом електронної пошти), мені здається це досить дивним, чи не вважаєте ви?
кругла криза

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

2

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

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

Можливо, команди не потребуватимуть такої обробки. Ініціатор команди, як правило, матиме доступ до призначеного виконавця команди. Це може бути, наприклад, у формі черги повідомлень до виконавця. Таким чином, команда призначена для однієї сутності .


1

Мені здається, щось додати до відповіді Квентіна-Сантіна, це те, що

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

Джерело .


0

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

Наприклад, уявіть a GenerateRandomNumber команду. Кожного разу, коли воно викликається, воно видаватиме інше випадкове число X. Таким чином, якщо ваш стан залежить від цього числа, кожен раз, коли ви переобчислюєте свій стан з історії команд, ви отримуватимете інший стан.

Події вирішують цю проблему. Коли ви виконуєте команду, вона створює послідовність подій, що представляють результат виконання команди. Наприклад, GenerateRandomNumberкоманда може створити файлGeneratedNumber(X) подію, яка реєструє сформоване випадкове число. Тепер, якщо ви перерахуєте свій стан із журналу подій, ви завжди отримаєте однаковий стан, тому що ви завжди будете використовувати той самий номер, який був створений певним виконанням команди.

Іншими словами, команди - це функції з побічними ефектами, події фіксують результат конкретного виконання команди.

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


0

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

Команди спрямовані на конкретний процесор. Таким чином, існує певний рівень залежності / зв’язку з ініціатором команди та процесором.

Наприклад, UserServiceпри створенні нового користувача команда надсилає команду "Надіслати електронну пошту" EmailService.

Той факт, що UserServiceзнає, що йому це потрібно EmailService, це вже зчеплення. Якщо EmailServiceзмінює свою схему API або йде вниз, це безпосередньо впливає на UserServiceфункцію.


Події не спрямовані на якийсь конкретний обробник подій. Таким чином видавець подій стає вільно пов'язаним. Йому байдуже, яка послуга споживає його подію. Навіть дійсно мати 0 споживачів події.

Наприклад, UserServiceпри створенні нового користувача публікується "Подія, створена користувачем". Потенційно EmailServiceможе використати цю подію та надіслати електронне повідомлення користувачеві.

Тут UserServiceневідомо про EmailService. Вони повністю розв’язані. Якщо EmailServiceзниження або зміна ділових правил, нам потрібно лише відредагуватиEmailService


Обидва підходи мають достоїнства. Архітектурний дизайн, що керується виключно подіями, важче відстежити, оскільки він занадто вільно зв'язаний, особливо у великих системах. А командна важка архітектура має високий рівень зчеплення. Тож хороший баланс ідеальний.

Сподіваюся, що це має сенс.

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