Экспресс-курс · No. 03
Архитектуру почти никогда не изобретают. Её узнают: какая из известных форм подходит к задаче — и какую цену эта форма за себя берёт.
17 паттернов · схема у каждого · шпаргалка в конце
Паттерн — это проверенный ответ на проблему, которая повторяется в тысячах систем. У каждого паттерна есть имя — и почти всегда это английское имя, по которому его ищут в документации и обсуждают на собеседованиях.
Паттерн — это имя для ответа, а не правило
В шахматах ты не изобретаешь дебют каждую партию. Ты узнаёшь: «это сицилианская защита» — и уже знаешь, куда она обычно ведёт.
Архитектурный паттерн (architectural pattern) = повторяющаяся проблема + проверенная форма решения + известная цена. Когда ты говоришь «сервисам нужно реагировать на то, что происходит в другом месте», это уже решённая и названная задача — паттерн Event-driven architecture. Знать каталог — значит перестать изобретать и начать выбирать.
Архитектурные паттерны ≠ паттерны проектирования
Паттерн проектирования — как расставить мебель в одной комнате. Архитектурный паттерн — планировка всего дома.
Паттерны проектирования (design patterns) — Factory, Observer, Strategy — живут внутри кода одного модуля. Архитектурные паттерны решают, какие большие «коробки» есть в системе и какие линии между ними: что разворачивается отдельно, кто с кем разговаривает, где живут данные. Этот курс — про планировки, а не про мебель.
Карта: 17 паттернов в шести группах
Карта метро: сначала видишь линии целиком, потом едешь по станциям.
Все паттерны курса делятся на шесть групп. Каждая группа отвечает на свой вопрос: как устроить одно приложение, куда смотрят зависимости, как резать систему, как части общаются, как хранить правду в данных и какие формы нужны под редкие задачи.
Как читать каждый паттерн
Паспорт: одинаковые поля у всех, поэтому их легко сравнивать.
У каждого паттерна дальше одно и то же: английское название (запомни его — это термин), образ из жизни, схема и карточка с полями: проблема (что болит), идея (как решает), пример, цена (что ты платишь), когда брать и связи с другими паттернами. Главное правило: лучшего паттерна не бывает — бывает подходящий, и у каждого есть счёт, который надо прочитать до того, как подписал.
Ты выбираешь не знаменитый паттерн. Ты выбираешь тот, чью цену можешь себе позволить.
Большинство систем начинаются как одно разворачиваемое приложение. Первые три паттерна — о том, как устроить его изнутри, чтобы оно не превратилось в кашу.
Layered Architecture — слоистая архитектура
Ресторан: зал принимает заказ, кухня готовит, кладовая хранит. Официант не готовит, повар не бегает в погреб — каждый говорит только со слоем под собой.
Самый распространённый паттерн. Код делится на горизонтальные слои (layers), и запрос проходит сквозь них сверху вниз. Каждый слой знает только про слой ниже.
- Проблема
- Весь код в одной куче: изменения в интерфейсе ломают работу с базой.
- Идея
Разложить код по этажам: представление, бизнес-логика, доступ к данным, база.
- Пример
Веб-приложение:
ControllerзовётService, тот —Repository, тот — Postgres.- Цена
- Всё разворачивается одним куском; мелкая правка = передеплой всего.
- Когда брать
- Почти всегда как стартовая форма для одного приложения.
- Связи
Основа для Modular Monolith; внутри слоёв часто применяют Hexagonal.
Modular Monolith — модульный монолит
Многоквартирный дом: одна крыша и один вход, но у каждой квартиры свои стены и дверь. Через спальню соседа к лестнице не ходят.
По-прежнему одно приложение, но внутри оно разрезано на модули (modules) по бизнес-смыслу — биллинг, каталог, аккаунты. Модули общаются только через публичный интерфейс и не лезут в чужие таблицы.
- Проблема
- Монолит превращается в «большой ком грязи»: всё зависит от всего.
- Идея
- Режем по бизнес-модулям с чёткими интерфейсами, но деплоим одним куском.
- Пример
Модуль
catalogвызывает только публичные функцииbilling, а не его таблицы.- Цена
Нужна дисциплина границ (их легко нарушить, если не проверять инструментом).
- Когда брать
Правильный дефолт почти для всех — ясность микросервисов без сети.
- Связи
Естественно вырастает в Microservices: модуль выносится в сервис, когда нужно.
MVC / MVVM — держать экран подальше от мозгов
Театр: сценарий, актёры с режиссёром и сцена со светом — разные работы. Декорации меняют, не переписывая пьесу.
Эти паттерны ставят стену между интерфейсом и данными с правилами, чтобы изменение того, как что-то выглядит, не ломало то, что оно делает.
- Проблема
- Логика размазана по экранам; поменять дизайн = переписать правила.
- Идея
Model — данные и правила, View — отображение, Controller (или ViewModel) — клей между ними.
- Пример
Django и Rails построены на MVC; мобильные и богатые веб-приложения — на MVVM, где ViewModel держит состояние экрана.
- Цена
В больших экранах «клей» разрастается; нужно следить, чтобы логика не утекала во View.
- Когда брать
- Любое приложение с интерфейсом.
- Связи
- Это слои Layered, взятые для UI.
Монолит — это не бардак. Бардак — это монолит без внутренних стен.
Два паттерна — одна идея: бизнес-правила в центре и ничего не знают про фреймворк, базу и веб. Всё техническое — снаружи и подключается к ядру.
Hexagonal Architecture — порты и адаптеры
Игровая консоль со стандартными разъёмами. Игре всё равно, что воткнули — геймпад, руль или аркадный стик; она просто читает «ввод».
Ядро с бизнес-логикой объявляет порты (ports) — интерфейсы вроде «сохранить заказ» или «списать деньги». Снаружи адаптеры (adapters) реализуют эти порты под конкретную технологию. Driving adapters вызывают ядро (HTTP API, CLI, тесты), driven adapters вызываются ядром (база, платёжка, почта).
- Проблема
Бизнес-логика вросла во фреймворк и базу — её невозможно тестировать и менять.
- Идея
Ядро говорит только с портами (интерфейсами); технология живёт в адаптерах.
- Пример
Порт
OrderRepository: в проде адаптер на Postgres, в тестах — список в памяти.- Цена
- Больше интерфейсов и файлов; для крошечного CRUD это избыточно.
- Когда брать
Есть настоящие бизнес-правила, которые должны пережить смену технологий.
- Связи
- Clean / Onion — та же идея, нарисованная кругами.
Clean / Onion Architecture — зависимости смотрят внутрь
Луковица: сердцевина в центре, слои вокруг. Шелуху можно снять, не повредив сердцевину, — а сердцевина от шелухи не зависит.
Тот же принцип в виде кругов. В центре сущности (entities), вокруг сценарии (use cases), дальше адаптеры, снаружи фреймворки и база. Главное правило — dependency rule: каждая стрелка зависимости смотрит к центру.
- Проблема
- Смена фреймворка или базы заставляет переписывать бизнес-логику.
- Идея
- Dependency rule: внутренние круги не импортируют ничего из внешних.
- Пример
Правило «счёт больше 10k требует согласования» — обычный класс без импортов веб-фреймворка и ORM.
- Цена
- Больше слоёв и маппинга данных между ними.
- Когда брать
- Долгоживущие продукты со сложными правилами.
- Связи
- Близнец Hexagonal; часто живёт внутри модулей Modular Monolith.
Фреймворки — это детали. Относись к ним как к обоям, а не как к фундаменту.
На каком-то масштабе — нагрузки или числа команд — одно приложение перестаёт справляться. Эти паттерны режут систему на независимые части и взамен вручают тебе сеть со всеми её сбоями.
Microservices — микросервисы
Фуд-корт: у каждой точки своя кухня, меню и касса. На пиццу налегли — ставят двух поваров; салатной это не нужно.
Система разбита на маленькие сервисы (services): каждый владеет одной бизнес-способностью и своей базой, разворачивается и масштабируется отдельно. Сервисы общаются по сети — HTTP или через шину сообщений.
- Проблема
Много команд мешают друг другу в одном коде; части системы нагружены очень неровно.
- Идея
- Отдельные сервисы с отдельными базами и отдельным деплоем.
- Пример
Сервисы
orders,inventory,delivery; в «чёрную пятницу» масштабируют только заказы и оплату.- Цена
Распределённая система: задержки сети, нет простых транзакций между сервисами, сложная эксплуатация и отладка.
- Когда брать
Большие команды и неровная нагрузка. Для стартапа из пяти человек — перебор.
- Связи
Вырастает из Modular Monolith; общается через Event-driven и Queues; для общих операций нужна Saga.
Serverless — функции по требованию
Вместо своей машины, которая стоит без дела, ты вызываешь такси и платишь только пока тикает счётчик.
Ты пишешь маленькие функции (functions), а облако запускает их по событию, само масштабирует от нуля до тысяч копий и обратно и берёт деньги за каждый запуск.
- Проблема
- Сервер простаивает и стоит денег, а нагрузка приходит рывками.
- Идея
- Код запускается только на событие; масштабированием занимается облако.
- Пример
Загрузили картинку в хранилище → функция делает превью → между загрузками ничего не работает.
- Цена
Cold start (задержка первого запуска), лимиты по времени, привязка к облаку.
- Когда брать
Рваная, событийная работа: обработка файлов, вебхуки, задачи по расписанию.
- Связи
- Естественно работает в паре с Event-driven.
Распределённые системы превращают вызовы функций в вызовы по сети. И каждый из них может упасть.
Как только частей больше одной, архитектура — это в основном линии между ними. То, как части разговаривают, решает, как они падают.
Request–Response — спросить и ждать
Заказ у стойки: спросил, стоишь, получил кофе — только потом идёшь дальше.
Клиент отправляет запрос (request) и ждёт ответ (response). Это синхронное общение: REST, gRPC, запрос в базу.
- Проблема
- Нужен ответ прямо сейчас, чтобы продолжить.
- Идея
- Вызвал → дождался → продолжил.
- Пример
- Оформление заказа вызывает платёжный API и ждёт подтверждения.
- Цена
Связанность во времени: если сервер тормозит или лежит, вызывающий тоже застрял.
- Когда брать
- По умолчанию — когда ответ действительно нужен сразу.
- Связи
- Противоположность асинхронным Event-driven и Queues.
Event-driven Architecture — объяви, а слушатели отреагируют
Редакция новостей: вышла новость — спорт, погода и реклама реагируют сами. Диктор их не ждёт и даже не знает, кто слушает.
Компонент не вызывает других, а публикует событие (event) — факт, который уже случился: «OrderPlaced». Кому интересно, тот подписан и реагирует. Два варианта: broker (события расходятся свободно) и mediator (координатор ведёт шаги по порядку).
- Проблема
Один сервис вынужден знать и вызывать всех, кому важно, что что-то случилось.
- Идея
- Публикуй факт; подписчики реагируют сами и независимо.
- Пример
- «OrderPlaced» → письмо клиенту, резерв на складе, запись в аналитику.
- Цена
Поток трудно отследить и тестировать; события приходят дважды или не по порядку.
- Когда брать
Много независимых реакций на одно действие; медленные побочные эффекты.
- Связи
Работает поверх Queues / Pub-Sub; основа для Event Sourcing и Saga.
Message Queue и Pub/Sub — почта между частями
Почта держит письмо, пока получатель не готов. Ты бросил его в ящик и ушёл; он прочитает, когда проснётся.
Очередь сообщений (message queue) или брокер pub/sub встаёт между отправителем и получателем, чтобы им не нужно было быть онлайн одновременно. В очереди одно сообщение берёт один worker; в pub/sub одно сообщение получают все подписчики.
- Проблема
Медленная работа тормозит ответ пользователю; пики нагрузки валят систему.
- Идея
- Положить задачу в очередь и обработать позже, в своём темпе.
- Пример
Тяжёлый отчёт кладут в очередь (RabbitMQ, Kafka, SQS), worker делает его в фоне, сайт остаётся быстрым.
- Цена
Результат становится отложенным; нужно думать про повторы и дубли сообщений.
- Когда брать
- Фоновые задачи, сглаживание пиков, связь между сервисами.
- Связи
- Транспорт для Event-driven и Microservices.
Pipe-and-Filter — конвейер для данных
Линия розлива: помыть, налить, закрыть, наклеить этикетку. Каждая станция делает одно и передаёт бутылку дальше.
Данные текут через цепочку независимых шагов — фильтров (filters), соединённых трубами (pipes). Каждый фильтр делает одно преобразование.
- Проблема
- Сложная обработка данных превращается в одну огромную функцию.
- Идея
- Разбить на простые шаги, каждый со входом и выходом.
- Пример
grep | sort | uniq, ETL-задача, компилятор (токенизация → разбор → оптимизация → генерация).- Цена
- Плохо подходит, когда шагам нужно возвращаться назад или договариваться.
- Когда брать
- Работа — ясная последовательность преобразований.
- Связи
- Шаги часто соединяют через Queues.
Синхронно — это телефонный звонок. Асинхронно — это письмо.
Эти паттерны не про коробки, а про то, как ты пишешь, читаешь и хранишь правду.
CQRS — раздельные модели записи и чтения
В библиотеке быстрый каталог для поиска и отдельная задняя комната, где книги расставляют по полкам. Поиск заточен под одно, хранение — под другое.
CQRS (Command Query Responsibility Segregation) разделяет модель записи — команды (commands), которые меняют данные, — и модель чтения — запросы (queries), которые их достают. Каждую можно оптимизировать и масштабировать отдельно.
- Проблема
Одна модель данных плохо подходит и для сложной записи, и для быстрого чтения.
- Идея
Две модели: запись — со всеми правилами, чтение — заранее посчитанные «витрины».
- Пример
Дашборд читает из денормализованной витрины, заказы пишутся в транзакционную базу.
- Цена
Две модели надо синхронизировать; чтение может слегка отставать (eventual consistency).
- Когда брать
Чтений кратно больше записей, или они очень разные по форме. Не по умолчанию.
- Связи
Часто в паре с Event Sourcing: события из лога строят модель чтения.
Event Sourcing — хранить историю, а не состояние
Банк хранит не только твой баланс, а каждый приход и расход. Баланс — лишь их сумма. Потеряешь его — пересчитаешь по журналу.
Вместо того чтобы перезаписывать текущее состояние, ты хранишь поток событий (event log), только дописывая в конец. Текущее состояние получается проигрыванием (replay) событий.
- Проблема
- Нужно знать не только «что сейчас», но и «как к этому пришли».
- Идея
- Источник правды — журнал событий; состояние вычисляется из него.
- Пример
Счёт: «открыт, +100, −30, +5» вместо «баланс: 75» — полный аудит и ответ на «как было в прошлый вторник?».
- Цена
Запросы «что сейчас» сложнее; события нельзя редактировать — история навсегда.
- Когда брать
- Финансы, аудит, сложные бизнес-процессы.
- Связи
- Почти всегда с CQRS; естественно сочетается с Event-driven.
Saga — длинная операция, которую можно отмотать
Бронируешь поездку: рейс, отель, машина. Машина сорвалась — отменяешь отель и рейс. Единой кнопки «отменить всё» нет, отматываешь по шагу.
Когда одно бизнес-действие проходит через несколько сервисов со своими базами, одну общую транзакцию сделать нельзя. Saga — цепочка локальных транзакций; если шаг падает, запускаются компенсирующие действия (compensations), отменяющие предыдущие шаги.
- Проблема
Одна операция затрагивает несколько сервисов, и нет общей транзакции.
- Идея
- Шаги по очереди, у каждого — «отменяющий» шаг на случай сбоя.
- Пример
Создать заказ → зарезервировать товар → списать с карты; списание упало — освободить товар, отменить заказ.
- Цена
Каждому шагу нужна компенсация; между шагами данные временно несогласованы.
- Когда брать
- Бизнес-операции поперёк Microservices.
- Связи
Нужна Microservices; шаги часто координируют через Event-driven.
Транзакция — это один атом намерения. Когда он охватывает сервисы, атом приходится собирать самому.
Эти паттерны существуют ради одной конкретной боли. Тянись за ними, когда у тебя именно эта боль — и ни секундой раньше.
Microkernel — маленькое ядро и плагины
Дрель: один мотор и дюжина насадок. Дрель та же — меняешь насадку под задачу.
Минимальное ядро (core) даёт основу, всё остальное приходит плагинами (plugins), которые подключаются к ядру и не зависят друг от друга.
- Проблема
- Функций много, они меняются и у разных клиентов разные.
- Идея
- Стабильное маленькое ядро + независимые плагины.
- Пример
VS Code и браузеры с расширениями; страховая система, где правила региона — плагин.
- Цена
- Нужно аккуратно спроектировать API для плагинов и их совместимость.
- Когда брать
- Продукт-платформа, кастомизация под клиентов.
- Связи
- Плагины часто подключаются через порты, как в Hexagonal.
Space-based Architecture — данные в памяти без узкого места
Стадион с двадцатью кассами, у каждой своя копия схемы зала, — вместо одной кассы с огромной очередью.
Когда единственная база становится узким местом под огромной нагрузкой, работу делят много processing units, каждый держит данные в памяти. Память синхронизируется между ними (data grid), а в базу данные пишутся позже, асинхронно.
- Проблема
- Одна база не выдерживает пиковой одновременной нагрузки.
- Идея
Данные в памяти многих узлов, синхронизация между ними, запись в базу — потом.
- Пример
- Флеш-распродажа или онлайн-аукцион с валом одновременных ставок.
- Цена
- Очень сложно и дорого; риск потери данных между памятью и базой.
- Когда брать
- Только при по-настоящему огромной и рваной конкурентной нагрузке.
- Связи
- Использует кеширование и асинхронную запись, как Queues.
Peer-to-Peer — без центра
Ужин в складчину: каждый и повар, и гость. Ни ресторана, ни официанта — группа кормит себя сама.
Узлы — peers — общаются напрямую, без центрального сервера. Каждый одновременно и клиент, и сервер.
- Проблема
- Центральный сервер — дорогая и уязвимая точка отказа.
- Идея
- Участники сами делят нагрузку и данные между собой.
- Пример
BitTorrent: каждый, кто скачивает, ещё и раздаёт — чем больше людей, тем сильнее сеть.
- Цена
- Трудно обеспечить согласованность, безопасность и контроль.
- Когда брать
- Раздача файлов, блокчейн, устойчивость без центрального хоста.
- Связи
- Противоположность клиент-серверным формам вроде Request–Response.
Экзотические паттерны — лекарство, а не витамины. Принимай под поставленный диагноз.
Паттерны — словарь, а не меню с одним блюдом. Реальные системы сочетают несколько, а самый мудрый ход часто самый простой.
Паттерны связаны между собой
Дом сразу использует несущие стены, водопровод и проводку — не «один истинный паттерн строительства».
Паттерны не живут поодиночке. Модульный монолит вырастает в микросервисы; микросервисам нужны события, очереди и саги; Hexagonal и Clean — одна идея; CQRS и Event Sourcing ходят парой.
Реальная система может быть модульным монолитом снаружи, Clean Architecture в каждом модуле, Request–Response для интерфейса, событиями для медленных побочных эффектов и очередью перед тяжёлой задачей. Вопрос не «какой паттерн», а «какие паттерны и где».
Начинай с самой простой формы
Под домик не заливают фундамент для стадиона.
Почти любая система должна начинаться как Modular Monolith и заслуживать свою сложность. Microservices, CQRS, Event Sourcing, Space-based — ответы на проблемы масштаба и размера команды, которых у тебя может никогда не быть. Самая дешёвая распределённая система — та, которую ты не построил.
- Какую повторяющуюся проблему решает эта форма? Назови одним предложением.
- Есть ли у меня эта проблема сейчас — или я гадаю про будущее?
- Чего это стоит? В задержках, эксплуатации, сложности, деньгах.
- Какой самый простой паттерн ещё сработал бы? Начни с него.
- Могу ли я взять это позже, когда боль реально проявится?
- Сервисов больше, чем инженеров.
- Одно действие пользователя задевает пять сервисов и две очереди.
- CQRS или Event Sourcing появились раньше, чем проблема чтения или аудита.
- Никто в команде не нарисует систему по памяти.
- Паттерн выбран, потому что так делают большие компании.
Шпаргалка: все 17 паттернов
Устройство одного приложения
- Layeredслоистая, n-tier
Код по этажам: представление → логика → данные → база. Запрос идёт сверху вниз.
- Modular Monolithмодульный монолит
Одно приложение, разрезанное на модули с чёткими интерфейсами. Лучший дефолт.
- MVC / MVVMModel–View–Controller
Стена между интерфейсом и данными с правилами.
Направление зависимостей
- HexagonalPorts and Adapters
Ядро объявляет порты, технологии подключаются адаптерами.
- Clean / Onionчистая / луковая
Круги, все зависимости смотрят к центру (dependency rule).
Разделение на части
- Microservicesмикросервисы
Отдельные сервисы со своими базами и деплоем. Цена — распределённая система.
- ServerlessFaaS
Функции по событию, облако масштабирует, платишь за запуск.
Как части разговаривают
- Request–Responseсинхронный вызов
Спросил и ждёшь ответа. Просто, но связывает во времени.
- Event-drivenсобытийная
Публикуешь факт, подписчики реагируют сами.
- Message Queue / Pub-Subочередь сообщений
Задача ждёт в очереди, пока worker не освободится.
- Pipe-and-Filterконвейер
Данные проходят через цепочку простых шагов.
Данные
- CQRSCommand Query Responsibility Segregation
Отдельные модели для записи и чтения.
- Event Sourcingсобытийное хранение
Храним журнал событий; состояние — его проигрывание.
- Sagaсага
Шаги через несколько сервисов + компенсации при сбое.
Особые формы
- Microkernelплагинная архитектура
Маленькое ядро и независимые плагины.
- Space-baseddata grid в памяти
Данные в памяти многих узлов, база не узкое место.
- Peer-to-PeerP2P
Узлы общаются напрямую, без центра.
Лучшая архитектура — самая простая, что переживает твои реальные требования, и ни одним паттерном больше.