Перейти к содержанию

Topology Architecture

Как система разбита на deployable units (приложения, сервисы, процессы, базы, очереди, внешние интеграции) и где они развернуты.

Группа Стиль Что это
Монолитные Monolith Один deployable unit, весь код в одном процессе
Сервисные SOA Крупные сервисы, общая шина (ESB), централизованное управление контрактами
Microservices Мелкие независимо деплоящиеся сервисы, у каждого своя БД, децентрализация
Self-Contained Systems (SCS) Вертикальные срезы «UI + логика + данные», слабо связанные между собой

Вход в систему держат API Gateway и BFF, между сервисами трафик ведут Service Discovery и Service Mesh.

Анти-паттерн всей оси - Distributed Monolith: сервисы нарезаны, но деплоятся и меняются только вместе.

Симптом -> что брать

Конкретную топологию берёшь, когда видишь конкретный симптом. Не наоборот.

  • Одна команда, домен ещё плывёт, гранулярность неясна -> модульный монолит, границы in-process.
  • Команда выросла, мешаете друг другу в одном репозитории и деплое -> дроби на сервисы по bounded context, а не по слоям.
  • Разным частям нужен независимый деплой или scale (одна CPU-bound, другая throughput-bound) -> выдели в отдельные сервисы ровно эти части.
  • Разным частям нужен разный runtime (Python для ML, Go для горячего пути) -> отдельный deployable там, где это реально окупается.
  • Куски системы развязаны по времени, есть пики, producer не должен ждать consumer -> EDA, события через broker.
  • Крупные автономные вертикали со своим UI, своими данными, минимум синхронной связи -> Self-Contained Systems.
  • Несколько разных клиентов (iOS, Android, web), каждый тянет свою форму данных -> BFF, по одному на UX, тонкий слой агрегации без домена.
  • Сервисы деплоятся и меняются всегда вместе -> ты собрал distributed monolith, схлопывай обратно.

Где ждёт overengineering

Дорогие ошибки почти всегда - это распределение там, где хватило бы вызова в памяти.

  • Преждевременные микросервисы. Раздробил до того, как понял границы, и получил distributed monolith: сервисы есть, а деплоятся и меняются вместе. Ломается не из-за числа сервисов, а из-за связности контрактов и данных.
  • Дробление по техническим слоям. Сервис «контроллеры», сервис «бизнес-логика», сервис «БД» - это three-tier, разнесённый по сети, и каждый бизнес-запрос дёргает все три. Режь по bounded context (вертикали), а не по слоям.
  • EDA там, где хватило бы вызова. Событие, на которое всегда подписан ровно один consumer, и ответ нужен синхронно - это request/response, переодетый в broker. Получил eventual consistency и отладку по логам брокера на ровном месте.
  • SCS или микросервисы на маленькую команду. Несколько вертикалей с отдельными UI и данными требуют людей на эксплуатацию. На команду из пяти человек это налог без отдачи.

Цена дробления, плати осознанно:

  • сеть вместо вызова: latency, ретраи, частичные отказы
  • распределённые данные: одной транзакции нет, вместо неё saga и eventual consistency
  • эксплуатация: трейсинг, версионирование контрактов, отдельные пайплайны на каждый сервис

Дробишь систему, не дробя организацию, - границы расползутся обратно по линиям общения людей (см. закон Конвея).

Ось: монолит <-> распределённость

Главный trade-off: внутрипроцессный вызов или сетевой.

Внутрипроцессный вызов:

  • быстрый: наносекунды, без сериализации и round-trip
  • надёжный: функция не отвалится по таймауту, частичных отказов нет
  • ACID-транзакция через границы модулей: один commit, один rollback, согласованность из коробки
  • простой деплой и отладка: один артефакт, один pipeline, один стектрейс, обычный дебаггер
  • безопасный рефакторинг через границы: IDE видит весь код, переименование и перенос логики проверяет компилятор

Сетевой вызов:

  • масштабируемо: каждый unit скейлится отдельно, под свою нагрузку
  • автономно: units деплоишь и релизишь независимо, команды не блокируют друг друга
  • но сеть ненадёжна: latency, таймауты, частичные отказы, retry, идемпотентность
  • ACID через границы units почти недостижим: вместо одной транзакции eventual consistency и saga с компенсациями
  • отладка распределённая: единого стектрейса нет, нужны distributed tracing и корреляция логов

N-tier: физические уровни

N-tier - деление системы на физические уровни: клиент, сервер приложений, БД на разных машинах. Это ось топологии, но не про монолит и сервисы: сервер приложений трёхуровневой системы может быть и монолитом, и набором сервисов.

Слой (layer) и уровень (tier) путают. Слой - логическое деление кода внутри одного процесса, это срез Code. Обычное веб-приложение - трёхуровневая система, у которой сервер приложений внутри устроен как монолит из трёх слоёв. Как отдельный выбор N-tier сегодня почти не встречается: любое веб-приложение с базой уже трёхуровневое. Термин остался с 90-х, когда от схемы «клиент-сервер» переходили к трём уровням.