Архитектурные паттерны
Архитектурные паттерны
Архитектурные паттерны — это проверенные решения для организации структуры программного обеспечения. Они определяют, как компоненты системы взаимодействуют друг с другом, как распределяются обязанности между модулями и как обеспечивается масштабируемость, поддерживаемость и надёжность приложения. Эти паттерны формируются на основе многолетнего опыта разработки и позволяют избежать типичных ошибок проектирования, упрощая процесс создания сложных систем.
Выбор архитектурного паттерна оказывает прямое влияние на жизненный цикл проекта: от этапа проектирования до сопровождения и модернизации. Правильно подобранная архитектура снижает стоимость изменений, упрощает тестирование, делает код более читаемым и предсказуемым. Архитектурные паттерны не диктуют конкретную реализацию, а задают общую форму, в рамках которой команда может принимать технические решения, соответствующие задачам проекта.
- Классический веб-сайт? Берём слоистую.
- Команда большая, масштабируем отдельные части? Микросервисы + события.
- Много событий и не надо ждать ответа? EDA (Event-Driven).
- Чтений в 100 раз больше, чем записей? CQRS.
- Боюсь привязаться к фреймворку / БД? Чистая / Гексагональная / Луковая.
- Фронтенд на React? Компонентная.
- Обработка логов / видео / ETL? Поток данных / Пайплайн.
- Условия меняются каждый день? На основе правил.
- Очень много потоков и надо чтоб не сдохло? Акторы.
- Спокойный REST API? Ресурсно-ориентированная.
- Хочу слабую связанность внутри монолита? Шина (MediatR).
- У меня редкая тяжёлая задача, не хочу держать сервер? Serverless.
Цель архитектурных паттернов
Основная цель архитектурных паттернов — создание устойчивой основы для разработки программного обеспечения. Эта основа должна выдерживать рост требований, изменения в бизнес-логике и эволюцию технологий. Архитектурный паттерн помогает разработчикам сосредоточиться на решении предметной задачи, а не на постоянной борьбе с хаосом в кодовой базе.
Паттерны обеспечивают разделение ответственности между частями системы. Это позволяет командам работать параллельно над разными модулями, не мешая друг другу. Разделение также упрощает локализацию ошибок и внедрение новых функций без необходимости переписывать значительную часть приложения.
Ещё одна важная цель — повышение предсказуемости поведения системы. Когда все участники проекта понимают, как устроена архитектура, они могут делать обоснованные предположения о том, где находится та или иная функциональность, как она реализована и как её можно расширить. Это особенно важно в крупных проектах с участием множества разработчиков.
Уровни архитектуры
Архитектура программного обеспечения рассматривается на нескольких уровнях. На самом высоком уровне находятся архитектурные паттерны, определяющие глобальную структуру системы. Ниже располагаются так называемые тактические решения — паттерны проектирования, такие как Singleton, Factory, Observer и другие, которые решают локальные задачи внутри модулей.
Архитектурные паттерны оперируют понятиями вроде слоёв, компонентов, сервисов, модулей, границ контекста. Они описывают, как эти элементы связаны между собой, по каким каналам происходит обмен данными, где хранится состояние системы и как оно изменяется. Тактические паттерны, напротив, работают с классами, объектами, интерфейсами и методами.
Важно понимать, что архитектурный паттерн не заменяет хорошие практики программирования. Он создаёт каркас, внутри которого должны применяться принципы чистого кода, SOLID, DRY и другие подходы к написанию качественного программного обеспечения. Без этого даже самая продуманная архитектура может превратиться в технический долг.
Слоистая архитектура (Layered Architecture)
Слоистая архитектура — один из самых распространённых и интуитивно понятных паттернов. В ней система делится на горизонтальные слои, каждый из которых выполняет определённую роль. Обычно выделяют три или четыре слоя: представление (Presentation), бизнес-логика (Business Logic), доступ к данным (Данные Access) и иногда инфраструктурный слой (Infrastructure).
Каждый слой зависит только от слоя, расположенного ниже него. Например, слой представления может обращаться к бизнес-логике, но не имеет прямого доступа к базе данных. Это обеспечивает чёткое разделение зон ответственности и упрощает модификацию отдельных частей системы.
Преимущества слоистой архитектуры включают простоту понимания, лёгкость тестирования отдельных слоёв и удобство распределения работы между разработчиками. Однако у неё есть и недостатки. Основной из них — потенциальное дублирование кода, когда одни и те же данные проходят через все слои, преобразуясь на каждом этапе. Также слоистая архитектура может ограничивать производительность, особенно в высоконагруженных системах, где каждый запрос проходит через несколько уровней абстракции.
Несмотря на эти ограничения, слоистая архитектура остаётся отличным выбором для большинства корпоративных приложений, где важна предсказуемость и поддерживаемость, а не максимальная скорость обработки запросов.
Коротко:
- Классика. Делишь приложение на слои: представление (UI), бизнес-логика, доступ к данным, база. Каждый слой знает только о том, что под ним.
- Контроллер вызывает сервис, сервис — репозиторий, репозиторий — БД. Обратно с данными. Слой выше не знает, как устроен слой ниже — только его интерфейс.
- Разделение ответственности + зависимость только сверху вниз.
- Большинство корпоративных веб-приложений (ASP.NET MVC, Spring Boot — классический «контроллер-сервис-репозиторий»).
Микросервисная архитектура (Microservices Architecture)
Микросервисная архитектура представляет систему как набор небольших, независимых сервисов, каждый из которых отвечает за конкретную бизнес-функцию. Эти сервисы общаются между собой через чётко определённые интерфейсы, чаще всего с использованием HTTP/REST или сообщений через очереди.
Ключевая идея микросервисов — декомпозиция монолита на автономные части, которые можно разрабатывать, тестировать, развёртывать и масштабировать независимо. Каждый микросервис имеет собственную базу данных, что исключает прямые зависимости между сервисами на уровне данных.
Такой подход даёт множество преимуществ. Команды могут выбирать технологии, наиболее подходящие для конкретной задачи, не будучи привязанными к единому стеку. Отказ одного сервиса не обязательно приводит к падению всей системы, если правильно реализованы механизмы отказоустойчивости. Масштабирование становится гибким: можно увеличить ресурсы только для тех сервисов, которые испытывают нагрузку.
Однако микросервисная архитектура значительно усложняет систему в целом. Появляются новые проблемы: управление распределёнными транзакциями, согласованность данных, отслеживание запросов между сервисами, сетевые задержки. Требуется развитая инфраструктура для мониторинга, логирования и оркестрации контейнеров. Микросервисы оправданы только в достаточно крупных проектах, где преимущества автономности перевешивают накладные расходы на управление сложностью.
Коротко:
- Всё разбито на маленькие независимые сервисы. У каждого своя база, свой стек, свой цикл деплоя.
- Сервисы общаются по сети (HTTP/REST, gRPC, очереди). Если один сдох — остальные работают.
- Независимость, масштабирование по частям, технологическая свобода.
- Крупные системы типа Netflix, Amazon, где команды большие и нагрузка зоопчая. Для подельника на 3 страницы — не надо.
Архитектура на основе событий (Event-Driven Architecture)
Архитектура на основе событий строится вокруг концепции событий — сигналов о том, что в системе произошло что-то значимое. Компоненты системы не вызывают друг друга напрямую, а публикуют события, на которые могут реагировать другие компоненты. Это создаёт слабую связанность между частями системы и повышает её гибкость.
Событийная архитектура часто использует брокеры сообщений, такие как Kafka, RabbitMQ или AWS SNS/SQS, для передачи событий между компонентами. Брокер обеспечивает надёжную доставку, буферизацию и маршрутизацию сообщений.
Одним из главных преимуществ событийной архитектуры является асинхронность. Производитель события не ждёт ответа от потребителей, что повышает отзывчивость системы. Это особенно полезно для сценариев, где требуется обработка большого объёма данных или выполнение длительных операций.
Событийная архитектура хорошо сочетается с микросервисами, позволяя им взаимодействовать без жёстких зависимостей. Она также поддерживает принцип "единственного источника истины" — каждое событие фиксирует факт, который уже произошёл, и не может быть отменено.
Однако работа с событиями требует особого подхода к проектированию. Необходимо учитывать возможные дубликаты сообщений, порядок их обработки и согласованность состояния. Отладка событийных систем сложнее, чем синхронных, поскольку поток выполнения распределён во времени и пространстве.
Коротко:
- Вместо прямых вызовов — кидаешь событие в брокер (Kafka, RabbitMQ). Кто подписался — тот реагирует.
- Заказ создан → событие
order_created→ отдельно уходят: уведомление, резерв товара, обновление аналитики. Всё асинхронно и независимо. - Слабая связанность + отзывчивость + масштабирование.
- Логи, аналитика, системы, где дохуя событий и не надо отвечать мгновенно.
Архитектура CQRS (Command Query Responsibility Segregation)
CQRS — это паттерн, который разделяет операции записи (команды) и чтения (запросы) на два разных канала. Вместо того чтобы использовать одну и ту же модель данных для изменения состояния и получения информации, CQRS предлагает две отдельные модели: одна для команд, другая для запросов.
Командная модель отвечает за валидацию, бизнес-правила и сохранение изменений. Запросная модель оптимизирована для быстрого извлечения данных и может быть представлена в виде денормализованных представлений, кэшей или даже отдельных баз данных.
Разделение позволяет оптимизировать каждую часть системы под свои задачи. Например, командная часть может использовать реляционную базу данных с транзакциями, а запросная — документную базу или колоночное хранилище для аналитики. Это особенно полезно в системах с высокой нагрузкой на чтение, где требования к производительности запросов сильно отличаются от требований к целостности данных.
CQRS часто используется вместе с событийной архитектурой. Изменения, внесённые командами, публикуются как события, которые затем обрабатываются для обновления запросных моделей. Это позволяет поддерживать согласованность между разными представлениями данных, хотя и с некоторой задержкой (eventual consistency).
Важно отметить, что CQRS добавляет значительную сложность и оправдан только в сложных доменах, где преимущества разделения перевешивают затраты на поддержку двух моделей.
Коротко:
- Разделяешь чтение и запись на разные модели. Запись через команды, чтение через запросы. Базы могут быть разные.
- Команда (
ChangeUserEmail) — идёт в одну БД (обычно нормализованную). Запрос (GetUserProfile) — читает из другой, денормализованной (быстрой). - Чтение и запись могут оптимизироваться независимо.
- Сстемы, где чтений намного больше, чем записей, и требования к производительности у них разные. Часто с Event Sourcing.
Архитектура "чистой" архитектуры (Clean Architecture)
Чистая архитектура — это подход, предложенный Робертом Мартином (Uncle Bob), направленный на достижение максимальной независимости от внешних деталей реализации. Основная идея заключается в том, что бизнес-логика должна быть полностью изолирована от фреймворков, баз данных, пользовательских интерфейсов и других инфраструктурных компонентов.
Архитектура строится как набор концентрических кругов, где внутренние слои ничего не знают о внешних. В центре находится домен — сущности и правила предметной области. Вокруг него располагаются варианты использования (use Кейсы), которые описывают, как система взаимодействует с внешним миром. Далее идут адаптеры интерфейсов — компоненты, преобразующие данные между внутренними структурами и внешними системами. На внешнем уровне находятся фреймворки и инструменты: базы данных, веб-серверы, UI-библиотеки.
Ключевой принцип чистой архитектуры — зависимость всегда направлена внутрь. Это означает, что код домена никогда не зависит от кода представления или доступа к данным. Такой подход обеспечивает высокую тестируемость: бизнес-логику можно проверять без запуска базы данных или браузера. Он также упрощает замену технологий: если понадобится перейти с одного ORM на другой или с REST на GraphQL, изменения затронут только внешние слои.
Чистая архитектура особенно полезна в долгосрочных проектах, где требования к бизнес-логике стабильны, а технологии быстро устаревают. Однако она требует дисциплины и понимания принципов инверсии зависимостей. Без этого легко нарушить границы между слоями, превратив архитектуру в формальность без реальной пользы.
Коротко:
- Бизнес-логика находится в центре и никак не зависит от внешних штук (БД, фреймворков, UI).
- Круги: сущности → варианты использования → адаптеры → фреймворки. Зависимости только внутрь. Внешнее можно менять без боли.
- Независимость от технологий. Тестировать бизнес-логику можно без базы и веб-сервера.
- Долгосрочные проекты, где технологии будут меняться, а ядро — стабильно.
Архитектура портов и адаптеров (Hexagonal Architecture)
Архитектура портов и адаптеров, также известная как гексагональная архитектура, была предложена Алистером Кокбурном. Она разделяет приложение на внутреннее ядро и внешние адаптеры, соединённые через порты — абстрактные интерфейсы.
Порт определяет, как ядро может взаимодействовать с внешним миром: например, «сохранить заказ» или «отправить уведомление». Адаптер реализует этот порт для конкретной технологии: один адаптер может использовать PostgreSQL для сохранения заказа, другой — MongoDB, третий — имитировать операцию в памяти для тестов.
Такой подход делает приложение независимым от деталей реализации. Ядро работает только с абстракциями, а выбор конкретного адаптера происходит на этапе сборки или конфигурации. Это позволяет легко подключать новые каналы взаимодействия: веб-API, CLI, мобильное приложение, очередь сообщений — все они становятся просто ещё одним адаптером.
Гексагональная архитектура хорошо сочетается с принципами Domain-Driven Проектирование (DDD), поскольку фокусируется на чистоте доменной модели. Она также упрощает интеграционное тестирование: вместо реальных сервисов можно подключать моки или заглушки через те же порты.
Основная сложность — необходимость проектировать чёткие интерфейсы портов с самого начала. Если порты слишком широкие или, наоборот, излишне детализированные, архитектура теряет гибкость. Кроме того, количество адаптеров может расти, что увеличивает объём кода и требует хорошей организации проекта.
Коротко:
- Вместо слоёв — ядро с портами (абстрактными интерфейсами), к которым подключаются адаптеры (конкретные реализации).
- Порт
UserRepository→ адаптеры:PostgresUserRepo,InMemoryUserRepo,ApiUserRepo. Ядро работает с портом — ему плевать, кто там внутри. - Приложение — это ядро, а внешний мир подключается через «ai-шные розетки».
- DDD-проекты, когда нужно легко подменять реализации (тесты, смена БД, новые каналы доступа).
Архитектура на основе компонентов (Component-Based Architecture)
Архитектура на основе компонентов рассматривает систему как набор автономных, заменяемых и многократно используемых компонентов. Каждый компонент инкапсулирует определённую функциональность и предоставляет чётко определённый интерфейс для взаимодействия с другими компонентами.
Компоненты могут быть реализованы как отдельные модули, библиотеки, микросервисы или даже физические устройства. Главное — соблюдение контракта: пока компонент удовлетворяет интерфейсу, его внутренняя реализация может меняться без влияния на остальную систему.
Такой подход способствует повторному использованию кода. Компонент, разработанный для одного проекта, может быть легко перенесён в другой, если он решает ту же задачу. Это особенно ценно в крупных организациях, где множество продуктов используют общие сервисы: аутентификацию, логирование, обработку платежей.
Архитектура на основе компонентов требует продуманной системы управления зависимостями. Компоненты должны иметь минимальные связи друг с другом, чтобы избежать эффекта домино при изменениях. Также важно стандартизировать форматы обмена данными, версионирование интерфейсов и механизмы обнаружения компонентов.
Этот паттерн широко применяется в современных фронтенд-фреймворках (React, Vue, Angular), где каждый элемент интерфейса — это компонент. На бэкенде он проявляется в виде модульных приложений, плагинов или расширяемых платформ.
Коротко:
- Система собирается из независимых, заменяемых компонентов. У каждого чёткий интерфейс.
- Компонент
Authотвечает за логин. КомпонентCart— за корзину. Они не знают друг о друге напрямую, общаются через чёткие контракты. - Переиспользование и замена частей без боли.
- Фронтенд-фреймворки (React, Vue, Angular) — там каждый UI-элемент компонент. На бэкенде — модульные приложения, плагины.
Архитектура потоков данных (Dataflow Architecture)
Архитектура потоков данных основана на движении данных через систему. Вместо вызова функций или методов данные проходят через последовательность обработчиков, каждый из которых выполняет определённую трансформацию. Такой подход часто используется в системах обработки сигналов, аналитики данных и потоковой передачи.
В чистом виде архитектура потоков данных реализуется через пайплайны: данные поступают на вход, проходят через цепочку этапов (фильтрация, агрегация, обогащение) и выдаются на выход. Каждый этап независим и может масштабироваться отдельно. Примеры таких систем — Apache Kafka Streams, Apache Flink, Spark Streaming.
Преимущества архитектуры потоков данных включают высокую производительность, параллелизм и отказоустойчивость. Поскольку данные движутся непрерывно, система может реагировать на события почти в реальном времени. Отказ одного обработчика не останавливает всю систему — данные могут буферизоваться и обрабатываться позже.
Однако проектирование таких систем требует глубокого понимания семантики данных, порядка событий и механизмов восстановления после сбоев. Отладка распределённых потоков сложнее, чем последовательного кода. Кроме того, не все задачи подходят для потоковой обработки — например, операции с жёсткими транзакционными гарантиями лучше реализовывать в других архитектурных стилях.
Коротко:
- Данные идут через цепочку обработчиков один за другим.
- Сырые логи → фильтр → агрегация → обогащение → сохранение. Каждый этап — отдельный обработчик.
- Последовательная трансформация данных. Легко распараллелить и масштабировать по шагам.
- Spark Streaming, Kafka Streams, ETL-процессы, компиляторы.
Архитектура на основе пространств имён (Onion Architecture)
Архитектура на основе пространств имён, или луковая архитектура, представляет собой развитие идей чистой архитектуры и гексагональной архитектуры. Система организована в виде слоёв, расположенных концентрически вокруг ядра — доменной модели. Каждый внешний слой зависит от внутреннего, но не наоборот.
В центре находятся сущности предметной области — классы, интерфейсы и правила, которые определяют бизнес-логику независимо от технологий. Вокруг них располагаются слои прикладной логики: сервисы, репозитории, обработчики команд. Затем идут инфраструктурные слои: реализации репозиториев, контроллеры API, адаптеры для внешних систем. На самом внешнем уровне — фреймворки, базы данных, пользовательские интерфейсы.
Ключевое отличие луковой архитектуры — полное отсутствие зависимостей от внешних библиотек в ядре. Даже такие распространённые элементы, как аннотации фреймворков или классы ORM, не должны проникать в домен. Это достигается через использование абстракций и внедрение зависимостей.
Луковая архитектура обеспечивает высокую степень изоляции бизнес-логики. Тестирование ядра возможно без поднятия базы данных, веб-сервера или сетевых соединений. Это ускоряет выполнение тестов и повышает их надёжность. Кроме того, такая структура делает код более устойчивым к изменениям в технологическом стеке.
Однако реализация луковой архитектуры требует тщательного планирования и дисциплины. Нарушение границ между слоями легко приводит к "утечке" инфраструктурных деталей в домен, что сводит на нет все преимущества. Также увеличивается количество шаблонного кода — адаптеров, мапперов, фасадов — что может замедлить разработку на ранних этапах проекта.
Коротко:
- Почти как чистая архитектура, ещё сильнее изоляция. В ядре — домен, вокруг него — сервисы приложения, потом инфраструктура. Никаких зависимостей от внешних фреймворков внутри.
- Ядро не использует даже аннотации ORM. Всё приходит через абстракции. Всё общение — через интерфейсы внутрь.
- Максимальная устойчивость к изменениям технологий.
- Сложные долгоживущие системы, где чистота домена важнее скорости кодинга.
Архитектура пайплайнов (Pipeline Architecture)
Архитектура пайплайнов строится вокруг последовательной обработки данных через цепочку этапов. Каждый этап принимает данные на вход, выполняет над ними определённую операцию и передаёт результат следующему этапу. Такой подход широко используется в компиляторах, системах обработки медиа, ETL-процессах и CI/CD-конвейерах.
Пайплайн состоит из источника данных, набора обработчиков и приёмника. Обработчики могут быть как синхронными, так и асинхронными. В асинхронных пайплайнах каждый этап работает независимо, получая данные из очереди и помещая результат в следующую очередь. Это позволяет масштабировать отдельные этапы в зависимости от нагрузки.
Преимущества архитектуры пайплайнов включают модульность, параллелизм и простоту расширения. Чтобы добавить новую функциональность, достаточно вставить новый этап в цепочку. Отказ одного этапа не обязательно останавливает всю систему — можно реализовать механизмы повторных попыток или обработки ошибок.
Однако проектирование эффективного пайплайна требует понимания потоков данных, балансировки нагрузки и управления состоянием. Если этапы сильно зависят друг от друга или требуют общего контекста, архитектура теряет гибкость. Также важно учитывать накладные расходы на передачу данных между этапами, особенно если они выполняются на разных машинах.
Коротко:
- Ступеньки, по которым шагают данные. Каждая ступенька выполняет действие и передаёт дальше.
- CI/CD пайплайн: линтер → тесты → сборка → деплой. Каждый этап независим.
- Модульность + легкое добавление новых этапов. Часто асинхронный.
- CI/CD (GitLab CI, Jenkins), системы обработки медиа (ffmpeg пайпы), ETL.
Архитектура на основе правил (Rule-Based Architecture)
Архитектура на основе правил применяется в системах, где поведение определяется набором декларативных правил, а не жёстко закодированной логикой. Правила представляют собой условия и действия: если выполняется условие, то выполняется действие. Такой подход используется в экспертных системах, движках бизнес-правил, системах маршрутизации и принятия решений.
Основные компоненты архитектуры — база правил, движок вывода и рабочая память. База правил содержит все доступные правила. Рабочая память хранит текущее состояние системы. Движок вывода сопоставляет факты из рабочей памяти с условиями правил и активирует соответствующие действия.
Преимущества архитектуры на основе правил — гибкость и удобство модификации. Изменение поведения системы не требует перекомпиляции кода — достаточно обновить правила. Это особенно ценно в динамичных средах, где бизнес-требования часто меняются. Правила также легче проверять и верифицировать, чем императивный код.
Однако производительность таких систем может быть ниже, особенно при большом количестве правил. Алгоритмы сопоставления (например, Rete) помогают оптимизировать процесс, но всё равно остаются накладные расходы. Кроме того, отладка и понимание поведения системы становятся сложнее по мере роста числа взаимодействующих правил.
Коротко:
- Поведение системы не закодировано жёстко, а задаётся правилами вроде «если X, то Y».
- База правил + движок вывода (например, Rete). Загружаешь новый набор правил — система ведёт себя иначе без перекомпиляции.
- Очень гибко, правила можно менять буквально на лету.
- Экспертные системы, скоринг заявок, бизнес-движки (Drools), системы антифрода.
Архитектура на основе акторов (Actor Model)
Архитектура на основе акторов — это модель параллельных вычислений, в которой система состоит из множества независимых единиц — акторов. Каждый актор имеет собственное состояние, обрабатывает сообщения и может создавать новые акторы или отправлять сообщения другим акторам.
Акторы не разделяют состояние. Единственный способ взаимодействия — асинхронная передача сообщений. Это исключает гонки данных и упрощает написание многопоточного кода. Акторы обрабатывают сообщения по одному, что гарантирует согласованность внутреннего состояния.
Такая архитектура хорошо масштабируется как вертикально, так и горизонтально. Она применяется в системах, требующих высокой отказоустойчивости и производительности: телекоммуникации, финансовые платформы, игры в реальном времени. Реализации модели акторов существуют во многих языках: Akka (Scala/Java), Orleans (.NET), Erlang/OTP.
Преимущества включают естественную параллелизацию, изоляцию сбоев и эластичность. Однако программирование в стиле акторов требует смены мышления: вместо вызова методов нужно думать в терминах сообщений и реакций. Отладка распределённых систем на основе акторов также представляет собой вызов, поскольку поведение зависит от порядка доставки сообщений.
Коротко:
- Система состоит из независимых акторов. У каждого своё состояние и очередь сообщений. Акторы общаются только сообщениями — никакой разделяемой памяти.
- Обрабатываешь сообщение → меняешь своё состояние → спамишь дальше акторам или создаёшь новых.
- Родной многопоток без гонок данных + отказоустойчивость.
- Erlang/OTP, Akka, Orleans. Телеком, игры, финансы, где нужна дохуя параллельность и живучесть.
Архитектура ориентированная на ресурсы (Resource-Oriented Architecture)
Архитектура, ориентированная на ресурсы, строится вокруг концепции ресурсов как основных единиц взаимодействия. Каждый ресурс имеет уникальный идентификатор, состояние и набор операций, которые можно над ним выполнять. Этот подход лежит в основе REST (Representational State Transfer) и широко применяется при проектировании веб-API.
Ресурсы представляют сущности предметной области: пользователи, заказы, товары, документы. Клиент взаимодействует с ресурсами через стандартные HTTP-методы: GET для получения, POST для создания, PUT/PATCH для обновления, DELETE для удаления. Ответы содержат представления ресурсов в форматах JSON, XML или других.
Ключевые принципы архитектуры, ориентированной на ресурсы, включают единообразие интерфейса, отсутствие состояния на сервере и кэшируемость ответов. Сервер не хранит контекст между запросами — вся необходимая информация передаётся в каждом запросе. Это упрощает масштабирование и повышает надёжность системы.
Преимущества такого подхода — простота интеграции, широкая поддержка инструментами и понятность для разработчиков. Однако важно соблюдать принципы REST, а не использовать его как просто способ передачи данных. Например, использование HTTP-методов не по назначению или игнорирование кодов состояния превращает API в RPC поверх HTTP, что лишает архитектуру её преимуществ.
Коротко:
- Основная единица — ресурс с уникальным ID. Ты делаешь CRUD через стандартные HTTP-методы.
GET /users/123,POST /users,DELETE /orders/456. Нет состояния на сервере. Ответы кешируются.- Просто, масштабируемо, понятно.
- 90% REST API по-нормальному (а не RPC через POST-ы).
Архитектура на основе шин (Bus Architecture)
Архитектура на основе шин использует центральный канал связи — шину — для обмена сообщениями между компонентами системы. Вместо прямых вызовов компоненты публикуют сообщения в шину, а другие компоненты подписываются на интересующие их типы сообщений.
Шина может быть реализована как программный модуль внутри приложения (например, MediatR в .NET) или как внешняя система (Kafka, RabbitMQ). Внутренние шины упрощают декомпозицию монолита, внешние — связывают распределённые сервисы.
Основное преимущество архитектуры на основе шин — слабая связанность. Компоненты не знают друг о друге, что упрощает модификацию и замену частей системы. Шина также обеспечивает буферизацию сообщений, маршрутизацию и преобразование форматов.
Однако централизованная шина может стать узким местом или точкой отказа. Необходимо тщательно проектировать схему сообщений, управление версиями и механизмы обработки ошибок. Без этого система может стать нестабильной или трудно поддерживаемой.
Коротко:
- Компоненты не общаются друг с другом напрямую, а шлют сообщения в центральную шину. Шина отправляет подписчикам.
- Сервис A публикует событие → шина → сервисы B и C, если подписаны.
- Слабая связанность + централизованная маршрутизация и буферизация.
- MediatR внутри монолита (in-memory bus), RabbitMQ/Kafka — между сервисами.
Архитектура на основе функций (Serverless / Function-as-a-Service)
Архитектура на основе функций предполагает разложение приложения на множество маленьких функций, которые выполняются по событию. Эти функции размещаются в облачной инфраструктуре (AWS Lambda, Azure Functions, Google Cloud Functions) и автоматически масштабируются в зависимости от нагрузки.
Функция — это автономный блок кода, который принимает входные данные, выполняет определённую задачу и возвращает результат. Она не хранит состояние между вызовами и должна быть идемпотентной. События, запускающие функции, могут поступать из очередей, баз данных, HTTP-запросов, таймеров и других источников.
Преимущества архитектуры на основе функций — экономическая эффективность (оплата только за время выполнения), автоматическое масштабирование и отсутствие необходимости управления серверами. Это особенно полезно для эпизодических задач: обработка загрузок файлов, отправка уведомлений, выполнение фоновых заданий.
Однако такой подход усложняет отладку, мониторинг и тестирование. Холодный старт функций может вызывать задержки. Также возникают проблемы с управлением зависимостями и безопасностью, поскольку каждая функция должна быть самодостаточной. Архитектура на основе функций лучше всего подходит для специфических сценариев, а не для построения всей системы целиком.
Коротко:
- Ты пишешь маленькие функции. Они лежат в облаке и запускаются по событию. Платишь только за время выполнения.
- Кинул файл в S3 → триггер → запустилась лямбда → конвертнула → сохранила. И всё. Нет ни сервера, ни даже контейнера твоего.
- Не думаешь про сервера, автомасштабирование от нуля до бесконечности.
- AWS Lambda, Cloud Functions. Обработка файлов, вебхуки, фоновые таски, трансформация данных.
Гибридные архитектуры
На практике большинство современных систем используют гибридные архитектуры, сочетающие элементы нескольких паттернов. Например, монолитное приложение может применять слоистую архитектуру внутри, но использовать события для интеграции с внешними сервисами. Микросервисы могут следовать принципам чистой архитектуры в своём внутреннем устройстве и использовать CQRS для работы с данными.
Гибридный подход позволяет выбирать лучший инструмент для каждой задачи. Нет универсального паттерна, подходящего для всех случаев. Успешная архитектура — это та, которая соответствует требованиям проекта, возможностям команды и ограничениям инфраструктуры.
Важно не смешивать паттерны хаотично, а осознанно комбинировать их, сохраняя целостность системы. Для этого необходимо чётко определять границы между частями системы, стандартизировать способы взаимодействия и поддерживать согласованность на уровне документации и кода.
В реальной жизни ты не выбираешь один паттерн. Ты делаем гибрид:
- Монолит внутри — слоистый + чистая архитектура + CQRS на одном участке.
- Микросервисы снаружи — внутри каждого свои паттерны.
- События между сервисами — EDA.
- Пару функций на Serverless — для тяжёлых файлов.
Поэтому, какой-то "серебряной пули" нет. Берём то, что решает конкретную задачу.
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). Паттерн — это повторяющийся шаблон, узор или схема. Паттерны встречаются повсюду — в природе, архитектуре, поведении людей и, конечно, в программировании. Порождающие паттерны проектирования — это группа шаблонов, направленных на решение задач, связанных с созданием объектов. Структурные паттерны — это группа шаблонов проектирования, решающих задачи организации классов и объектов таким образом, чтобы обеспечить гибкую архитектуру программного обеспечения. Поведенческие паттерны — это группа шаблонов проектирования, которые определяют способы взаимодействия объектов и распределения ответственности между ними. Интеграция систем — одна из центральных задач в современной разработке программного обеспечения. Паттерны доменного моделирования представляют собой проверенные решения для организации бизнес-логики в программных системах.Обзор паттернов проектирования
Порождающие паттерны
Структурные паттерны
Поведенческие паттерны
Паттерны интеграции внешних систем
паттерны проектирования доменных моделей