Назад до блогу
5 хв читання

Команди та події в мікросервісах: ключові відмінності

Повідомлення, якими мікросервіси обмінюються через брокер повідомлень (message broker), можна поділити щонайменше на дві категорії: команди (commands) та події (events). Брокер у вміст не заглядає: і команда, і подія для нього — просто повідомлення, тобто тіло (JSON, Avro, Protobuf тощо) і заголовки. Проте це різні концепції, і їх варто явно розрізняти, коли проєктуєте взаємодію між мікросервісами.

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

Що таке команди (Commands)

Уявімо систему з кількох мікросервісів. Мікросервіс Audit приймає від інших команду створити й зберегти запис аудиту (audit log entry). Так ми можемо відстежувати, хто з користувачів, що і коли зробив у системі. Ось високорівнева діаграма:

Два відправники надсилають ту саму команду CreateAuditLogEntry у чергу, а виконує її лише Audit

Коли мікросервісу Profile чи Order потрібно створити запис аудиту, він надсилає в брокер команду з потрібними даними. Мікросервіс Audit отримує цю команду і створює запис. Зазвичай команди надсилають у чергу (queue). Це модель point-to-point: кожне повідомлення з черги забирає лише один споживач.

Спробуймо виділити важливі характеристики команд:

  1. Команду можуть надсилати кілька відправників. І Profile, і Order надсилають у брокер команду, коли їм потрібно створити запис аудиту.
  2. Команда має лише одного споживача. Команду CreateAuditLogEntry обробляє тільки мікросервіс Audit. Ідеться про логічного споживача: Audit може працювати в кількох екземплярах, які конкурують за повідомлення з однієї черги (патерн Competing Consumers), але кожне повідомлення отримає лише один із них. Водночас «один споживач» не означає «рівно одна обробка»: більшість брокерів гарантують доставку at-least-once, тож та сама команда може прийти повторно, і обробник має бути ідемпотентним. Якщо ж ту саму команду обробляють кілька різних мікросервісів, це, найімовірніше, свідчить про проблему в архітектурі.
  3. Мета команди — викликати певну поведінку. Profile і Order надсилають команду, щоб Audit створив запис аудиту.
  4. Контракт команди належить споживачу. Лише мікросервіс Audit визначає схему команди й відповідає за неї. Відправники залежать від цього контракту: якщо Audit змінить схему, їм доведеться оновитися.
  5. Команда зазвичай містить усі дані, потрібні для виконання операції. Ці дані має надати відправник.
  6. Назва команди — дієслово в наказовому способі: CreateAuditLogEntry, SendEmail, ReserveStock. Один мікросервіс каже іншому, що зробити, тож назва звучить як розпорядження.

Ще одна важлива властивість команди: споживач може її відхилити. Наприклад, відправник помилково передав некоректні дані. Тоді споживач валідує команду, відхиляє її, і стан системи не змінюється. Але через брокер відправник не отримує відповіді одразу. Повідомити його про відмову можна лише окремим повідомленням — відповіддю (reply) у чергу відправника або подією на кшталт AuditLogEntryRejected. Якщо відправнику потрібно дізнатися результат негайно, простіше реалізувати команду синхронно — через HTTP або gRPC.

Що таке події (Events)

Тепер погляньмо на іншу діаграму, щоб зрозуміти, що таке подія:

Один видавець публікує подію ArticlePublished у топік, і кожен підписник отримує власну копію

У нашому прикладі мікросервіс Article публікує подію ArticlePublished. Мікросервіс Notification отримує цю подію і надсилає email-сповіщення всім підписникам автора. Мікросервіс Analytics отримує ту саму подію, щоб оновити статистику.

Тепер виділимо важливі характеристики подій:

  1. Подію публікує лише один видавець. Якщо ту саму подію публікують кілька різних мікросервісів, це може свідчити про порушення принципу єдиної відповідальності на рівні архітектури.
  2. Подія може мати нуль, одного або багато споживачів. Будь-яка кількість мікросервісів може підписатися на подію, щоб відреагувати на неї. Видавець не знає, хто саме підписаний, — і не повинен знати.
  3. Подія повідомляє решту системи, що щось сталося. Наприклад, ArticlePublished з’являється вже після того, як статтю опублікували. Споживачу лишається прийняти цей факт і відреагувати.
  4. Контракт події належить видавцю. Мікросервіс Article визначає схему події ArticlePublished і відповідає за неї. Підписники залежать від цього контракту, тобто залежність спрямована навпаки, ніж у команди.
  5. Подія містить дані про те, що сталося, але не дані, потрібні конкретному споживачу. Наприклад, ArticlePublished несе ідентифікатори статті та її автора, але не список email-адрес, на які треба надіслати сповіщення. Ці дані належать мікросервісу Notification: він сам знаходить підписників автора за authorId. Скільки даних класти в подію — окреме проєктне рішення. Це може бути мінімальне сповіщення лише з ідентифікаторами (event notification) або подія з повним станом сутності (event-carried state transfer), яка позбавляє споживачів зворотних запитів до видавця.
  6. Подія описує факт у минулому, тому її назва — дієслово в минулому часі: ArticlePublished, ArticleDeleted…

Підсумок

Зведімо відмінності між командами та подіями в одну таблицю:

КомандаПодія
Хто надсилаєОдин або кілька відправниківЛише один видавець
Хто отримуєРівно один споживачНуль, один або багато підписників
Канал у брокеріЧерга, point-to-pointТопік, publish-subscribe
МетаВикликати певну поведінку в конкретного споживачаПовідомити всіх зацікавлених, що щось сталося
Хто про кого знаєВідправник знає, хто виконає командуВидавець не знає своїх підписників
Власник контрактуСпоживачВидавець
ДаніУсе, що потрібно для виконання діїДані про сам факт
Стан системиСпоживач вирішує, виконати команду і змінити стан чи відхилити їїСтан уже змінився, споживачу лишається прийняти факт
НазваДієслово в наказовому способіДієслово в минулому часі

Як працювати з командами й подіями в Azure Service Bus і що робити з ідемпотентністю, retry та dead-letter, розбираємо на курсі «Microsoft Azure для розробників».

Це переклад моєї англомовної статті «Commands vs Events in Microservices», опублікованої на Medium. В українській версії додано, через які канали брокера зазвичай передають команди й події, і уточнено кілька формулювань.

ЩО ВИВЧАТИ ДАЛІ