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-х, когда от схемы «клиент-сервер» переходили к трём уровням.