Форма развёртывания — Примеры

Пример 1 — Вызов зависимости без тайм-аута

Профиль пользователя забирается из другого сервиса. Когда тот сервис замедляется, перестаёт отвечать всё приложение. Скажите, какой отказ граница не изолировала, затем сделайте отказ локальным.

async function getProfile(id: string) {
  const res = await fetchJson(userService + "/profile/" + id);   // ждёт бесконечно
  return { id, tier: res.tier };
}

// userService замедляется -> каждый обработчик запроса держит соединение

Решение

async function getProfile(id: string) {
  const res = await fetchJson(userService + "/profile/" + id, {
    timeoutMs: 200, retries: 1,
  }).catch(() => null);                     // ухудшенный режим, а не зависание
  return { id, tier: res?.tier ?? "standard" };
}

Разделение системы на части само по себе не изолирует отказ; оно создаёт новые режимы отказа, а вызывающий, который ждёт вечно, распространяет отказ через ту самую границу, которая должна была его защитить. Явный тайм-аут вместе с определённым ухудшенным ответом удерживают радиус поражения локальным: вызов профиля возвращает уровень по умолчанию, а остальное приложение продолжает обслуживать запросы. Этим и отличаются раздельные сервисы от независимых — разницу проектируют осознанно у каждого исходящего вызова.

Пример 2 — Распределённая система без названной причины

Небольшая команда делит приложение на шесть сервисов, потому что так выглядела эталонная архитектура, и теперь каждой функции нужны три согласованных развёртывания. Проверьте, было ли разделение оправданным, затем назовите подходящую форму.

сервисы: gateway, users, orders, billing, inventory, notifications
симптомы: изменение одного поля требует выпустить три сервиса по порядку;
          один падающий сервис утягивает за собой два других
названные причины разделения: «микросервисы — это современно»

Решение

возможные ограничения, оправдывающие разделение:
  - две команды должны поставлять в разном темпе        измеримо -> если так, разделите этот шов
  - конвейеру данных нужно собственное масштабирование  измеримо -> если так, извлеките его
  - «микросервисы — это современно»                     не ограничение -> границы нет

по умолчанию: одна развёртываемая единица, ясные внутренние границы, извлечение по потребности

Монолит с чистыми внутренними границами проще для понимания, тестирования и отладки, чем тот же код, разделённый по процессам, и при этом он оставляет вариант открытым: модуль можно извлечь позже, по одному, когда появится настоящая операционная потребность. Начать распределённо очень трудно откатить, и эта система заплатила операционные издержки шести развёртываемых единиц, сохранив связанность одной. Проверка проста: запишите, что нужно системе и чего не может дать одна единица — независимый темп релизов, другой профиль масштабирования, отказ, который нужно сдержать, — и разделяйте только там, где это предложение удаётся закончить.