Є багато рішень, які компромісують більше, ніж мені подобається. Зрозуміло, якщо ваш спосіб використання складний, наприклад, переміщення грошей між різними банками, більш приємні альтернативи можуть бути неможливими. Але давайте подивимось, що ми можемо зробити за загальним сценарієм, коли використання мікросервісів заважає нашим потенційним транзакціям із базами даних.
Варіант 1: Уникайте необхідності угод, якщо це можливо
Очевидно і згадувалося раніше, але ідеально, якщо ми можемо ним керувати. Чи належали компоненти насправді в одній мікросервісі? Або ми можемо переробити такі системи, щоб транзакція стала непотрібною? Можливо, прийняття транзакцій є найбільш доступною жертвою.
Варіант 2: Використовуйте чергу
Якщо є достатня впевненість у тому, що інша послуга матиме успіх у тому, що ми хочемо, ми можемо викликати її через певну форму черги. Елемент із черги не буде зібрано до пізніше, але ми можемо переконатися, що цей предмет у черзі .
Наприклад, скажіть, що ми хочемо вставити організацію та надіслати електронний лист, як одну транзакцію. Замість того, щоб дзвонити на поштовий сервер, ми ставимо чергу в електронній пошті в таблиці.
Begin transaction
Insert entity
Insert e-mail
Commit transaction
Очевидним недоліком є те, що декілька мікросервісів потребують доступу до однієї таблиці.
Варіант 3: Зробіть зовнішню роботу останньою, безпосередньо перед завершенням транзакції
Цей підхід ґрунтується на припущенні, що скоєння угоди малоймовірне не вдасться.
Begin transaction
Insert entity
Insert another entity
Make external call
Commit transaction
Якщо запити не вдається, зовнішній виклик ще не відбувся. Якщо зовнішній виклик не працює, транзакція ніколи не здійснюється.
Цей підхід має обмеження того, що ми можемо зробити лише один зовнішній дзвінок, і це потрібно зробити останньою (тобто ми не можемо використовувати його результат у наших запитах).
Варіант 4: Створення речей у стані очікування
Як розміщено тут , ми можемо з декількох мікросервісів створювати різні компоненти, кожен у відкладеному стані, без транзакцій.
Будь-яка перевірка виконується, але в остаточному стані нічого не створюється. Після того, як все було успішно створено, кожен компонент активується. Зазвичай ця операція настільки проста, і шанси на те, що щось піде не так, настільки малі, що ми навіть можемо вважати за краще робити активацію без транзакцій.
Найбільшим недоліком, мабуть, є те, що нам доведеться враховувати існування відкладених позицій. Будь-який запит вибору повинен врахувати, чи потрібно включати очікувані дані. Більшість мусить ігнорувати це. А оновлення - це зовсім інша історія.
Варіант 5: Нехай мікросервіс поділиться своїм запитом
Жоден з інших варіантів це не робить для вас? Тоді давайте неортодоксально .
Залежно від компанії, це може бути неприйнятним. Я знаю. Це неортодоксально. Якщо це не прийнятно, перейдіть іншим маршрутом. Але якщо це відповідає вашій ситуації, це вирішує проблему просто та потужно. Це може бути просто найбільш прийнятний компроміс.
Існує спосіб перетворити запити з декількох мікросервісів у просту, єдину транзакцію бази даних.
Поверніть запит, а не виконайте його.
Begin transaction
Execute our own query
Make external call, receiving a query
Execute received query
Commit transaction
Мережевий, кожен мікросервіс повинен мати доступ до кожної бази даних. Майте це на увазі, також стосовно майбутнього масштабування.
Якщо бази даних, що беруть участь в транзакції, знаходяться на одному сервері, це буде звичайною транзакцією. Якщо вони знаходяться на різних серверах, це буде розподілена транзакція. Код однаковий незалежно.
Ми отримуємо запит, включаючи його тип з'єднання, його параметри та рядок з'єднання. Ми можемо перетворити його в акуратний виконуваний клас Command, зберігаючи потік читабельним: виклик мікросервісу призводить до команди, яку ми виконуємо, як частина нашої транзакції.
Рядок з'єднання - це те, що дає нам початкова мікросервіс, тому для всіх намірів і цілей запит як і раніше вважається виконаним цією мікросервісом. Ми просто фізично прокладаємо це через мікросервіс клієнта. Це має значення? Ну, це дозволяє нам помістити його в ту саму транзакцію з іншим запитом.
Якщо компроміс прийнятний, такий підхід дає нам пряму транзакційність монолітного додатку в архітектурі мікросервісу.