Краткий обзор восьми наиболее востребованных архитектурных шаблонов с иллюстрациями:
- Многоуровневая архитектура;
- «Клиент-сервер»;
- «Каналы и фильтры»;
- «SOA»;
- «Издатель-подписчик»;
- «Общие данные»;
- «Одноранговая сеть»;
- «Брокер сервисов».
По своей сути — шаблоны представляют переиспользуемые решения для типичных задач. Когда перед нами возникает такая задача, то лучше сначала поискать готовое решение в каталоге существующих шаблонов и лишь потом переходить к созданию чего-то полностью нового. В случае же обнаружения подходящего варианта останется просто подстроить его под актуальную область задачи.
Использование шаблона проектирования можно сравнить с присутствием в вашей команде других архитекторов, от которых вы получаете знания, не вкладывая в это слишком много времени. В сфере разработки ПО существует масса всевозможных шаблонов, но в этой статье я решил сосредоточиться на наиболее используемых без привязки к конкретному языку.
Чем архитектурные шаблоны отличаются от шаблонов проектирования?
В названии статьи упоминаются архитектурные шаблоны, так что, думаю, следует прояснить их отличие от шаблонов проектирования, хотя это отличие может оказаться довольно неоднозначным. В первую очередь стоит отметить, что отличаются они типом решаемых с их помощью задач. Архитектурные шаблоны определяют решение для достижения различных качественных характеристик системы и подразумевают работу с несколькими компонентами ПО. В этом их область применения обширнее области шаблонов проектирования, которые предоставляют способ для структуризации классов с целью создания оптимальной внутренней структуры.
▍ Многоуровневый шаблон
Начнём с, пожалуй, наиболее востребованной многоуровневой архитектуры. Разделение системы на отдельные уровни с организацией внутри них компонентов по соответствующим критериям позволяет более слаженно взаимодействовать отдельным разработчикам и командам. В таком подходе уровни способствуют применению полезных практик слабого зацепления и высокой связности.
Строго говоря, в этой модели каждый модуль должен присваиваться только одному уровню, притом, что вышестоящие уровни могут использовать нижестоящие. Такая структура оправдывает себя в плане обслуживаемости, позволяя разным командам параллельно работать над разными модулями. Вот пример подобной схемы:
Многоуровневый шаблон
API взаимодействует с сервисами бизнес-логики, которые, в свою очередь, обращаются к уровню данных. При этом все они могут иметь общий набор библиотек для повторного использования компонентов.
▍ Шаблон «Клиент-сервер»
Тоже очень распространённый шаблон, в котором сервер предлагает один или более сервисов, потребляемых одним или более клиентами. Будучи простой, но в то же время очень мощной, эта модель даёт возможность построения распределённых систем, в которых сервер централизует ресурсы и рабочую нагрузку в одном месте, позволяя множеству потребителей использовать эти данные независимо.
На основе этого механизма работают мобильные приложения, Twitter, Medium, электронная почта, обмен файлами и Web в целом. В таком контексте пользовательские приложения потребляют данные и предоставляют интерфейс для взаимодействия с ними, в результате чего клиент и сервер формируют общую систему. Однако в рамках этого шаблона есть строгое распределение обязанностей и ответственностей.
Шаблон «Клиент-сервер»
▍ Шаблон «Каналы и фильтры»
В случае шаблона «Каналы и фильтры» каждый компонент-фильтр отвечает за одно преобразование данных или операцию с ними. Данные передаются от одного фильтра следующему максимально быстро, и операция над ними выполняется параллельно. Раздельные фильтры даже можно повторно использовать и менять местами для создания новых пайплайнов.
Этот шаблон широко применяется в анализе и преобразовании данных. Наиболее же типичный пример — это использование Unix-функции pipe
для комбинирования команд — суть та же.
Шаблон «Каналы и фильтры»
Здесь можно выделить:
- фильтр: компонент, который считывает данные, преобразует их и возвращает результат;
- канал: связующий компонент, переносящий данные от одного фильтра к другому и гарантирующий их неизменность на этом пути.
Этот шаблон может оказаться вычислительно затратным в силу того принципа, по которому он выполняет анализ данных. Однако при этом он предлагает быстродействие на более высоком уровне архитектуры за счёт того, что может выполнять пост-обработку, очистку и классификацию данных, понижая рабочую нагрузку в других более неотложных процессах.
▍ Шаблон SOA
В сервис-ориентированной архитектуре (SOA) независимые компоненты реализуются в виде сервисов, что предоставляет специфическую функциональность. Эти сервисы совмещаются в среде выполнения, определяя поведение системы в целом. Чтобы это сработало, потребители сервисов должны иметь возможность обнаруживать и использовать их, не зная деталей реализации.
Построить такую архитектуру можно по-разному.
Традиционные системы SOA в основном опираются на протокол SOAP, который работает путём обмена XML-сообщениями, а более «современные» приложения ориентированы на использование микросервисов, которые связываются легковесными сообщениями, передаваемыми по протоколу вроде HTTP.
Ниже приведён упрощённый пример представления системы SOA. На практике же подобные архитектуры сложны и включают множество компонентов. На схеме мы видим два сервиса, подключённых к общему реестру сервисов. Через этот реестр они при необходимости находят информацию для подключения друг к другу.
Обобщённый пример SOA
Эта архитектура повышает функциональную совместимость и масштабируемость системы, но также привносит сложность в процесс определения и интеграции распределённых систем, поскольку зачастую нелегко управлять изменениями в сообщениях, которые могут повлиять на потребителей разных сервисов.
▍ Шаблон «Издатель-подписчик»
В шаблоне «Издатель-подписчик» издатели и потребители данных существуют независимо и являются друг для друга анонимными. В рамках этого шаблона множество потребителей подписываются на события, публикуемые множеством издателей. Обе стороны взаимодействуют опосредованно через шину событий.
Поскольку все взаимодействия реализуются через шину событий, то к ней должны быть подключены все участники. Причём для успешной работы здесь важен правильный выбор подходящей технологии. Ниже показан пример такой системы, в которой разные типы устройств подключены к шине событий.
Шаблон «Издатель-подписчик»
Эта архитектура повышает переиспользуемость и быстродействие при обмене данными, оптимизируя их создание и потребление на шине событий. Однако сложно рассуждать о производительности таких систем, учитывая асинхронную природу коммуникаций. В конечном счёте шина событий оказывается узким местом в системах как с высоким, так и с низким быстродействием.
▍ Шаблон «Общие данные»
В шаблоне «Общие данные» несколько компонентов обращаются к набору данных через общее хранилище. При этом ни один из компонентов не несёт полной ответственности за сами данные или хранилище, так как они являются общими. Такой шаблон особенно эффективен в случаях, когда множеству компонентов требуется доступ к большим объёмам данных.
Шаблон «Общие данные»
И хотя эта схема имеет собственное название, обычно мы видим её в составе более крупных систем, например, когда в архитектуре SOA разные сервисы обращаются к общей базе данных. В таком случае можно определить типы доступа как чтение и запись, установив разные правила и разрешения, оптимизирующие и защищающие доступ к данным. Сегодня сложность этой архитектуры понижается, к примеру, с помощью облачных сервисов вроде AWS RDS, которые обеспечивают для баз данных работу с репликами, масштабируемость и резервное копирование.
Этот шаблон повышает надёжность за счёт согласованности данных, а также масштабируемость и быстродействие в случае их правильной разбивки. С другой стороны, в случае некорректного управления системой существует единая точка отказа.
▍ Шаблон P2P
Одноранговые архитектурные шаблоны принадлежат к категории симметричных шаблонов «Клиент-сервер». Симметричность в данном контексте означает отсутствие в сети подразделения на клиентов и серверы. В этом шаблоне одна система выступает и как клиент, и как сервер.
Каждая система, также называемая пиром, отправляет запросы другим пирам сети и в то же время получает и обслуживает запросы от других пиров этой сети. Такая схема сильно отличается от традиционной клиент-серверной сети, в которой клиент должен только отправлять запрос и ожидать его обработки сервером.
Одноранговая архитектура
Примеры этой архитектуры можно наблюдать в сетях обмена файлами вроде Gnutella, а также протоколе блокчейна и его реализации Bitcoin.
▍ Шаблон «Брокер сервисов»
Брокерская система включает в себя три основных компонента: брокера, сервер и клиента. Этот шаблон используется для структуризации распределённых систем с раздельными компонентами. На определённом уровне он является расширением клиент-серверного подхода для более сложных сценариев.
Брокер – это компонент, отвечающий за переправку сообщений между клиентом и сервером. Эти сообщения представляют собой запросы к сервисам и ответы на них, а также отчёты о возникших исключениях.
Серверы размещают информацию о своих возможностях (сервисы и характеристики) на брокере, который при получении от клиента определённого запроса перенаправляет этого клиента в подходящий сервис из имеющихся в реестре. Хорошими примерами брокеров сообщений являются Apache ActiveMQ, Apache Kafka, RabbitMQ.
Схематично шаблон выглядит так:
Шаблон «Брокер сервисов»
Использовать этот шаблон рекомендуется, когда связь между клиентом и сервером не является фиксированной ввиду присутствия нескольких подходящих серверов или их периодического изменения. Он также актуален, если выбор сервера зависит от определённого критерия, который достаточно сложен для передачи отдельному компоненту. Что же касается настройки или создания брокера, то это сложная задача, которая обычно выполняется одним из провайдеров вроде упомянутых выше.
▍ Поиск подходящей архитектуры
Список шаблонов можно продолжать и продолжать. В действительности я перечислил лишь те, что считаю наиболее распространёнными. Некоторые шаблоны рождаются в ходе решения новых задач, другие могут подходить для различных систем или команд, а третьи лишь одной организации. Важно объективно оценивать структуру вашей системы, а также обращаться к опыту других людей, знакомых с областью стоящей перед вами задачи. Скорее всего, ваше решение потребует комбинирования нескольких из перечисленных шаблонов, ибо сложность является неизбежным фактором во всех успешных системах ПО.
▍ Бонус: несоответствие архитектуры
Архитектурное несоответствие – это феномен, который очень часто встречается при проектировании архитектуры, в которой предполагается, что компонент будет использоваться способом, противоречащим его базовому назначению. В результате возрастает сложность не только разработки архитектуры, но и её обслуживания, что делает невозможным достижение необходимых критериев качества.
Такая ситуация может сложиться на концептуальном уровне, когда архитектурная схема не сопоставлена с наиболее важными атрибутами создаваемой системы, или когда выбрана неподходящая технология. Например, если ничто не указывает на связь архитектуры с моделью издатель-подписчик, то использование реляционной базы данных в качестве основного механизма обмена сообщениями существенно скажется на итоговом результате.
Изучение шаблонов и выделение необходимого времени на подобающее определение архитектуры может избавить от множества проблем, так что не стоит недооценивать этот этап в начале каждого проекта. Даже выбор необходимых технологий нужно производить лишь тогда, когда определена архитектура, но не наоборот, как это часто бывает.
Вот, собственно, и всё. Благодарю за внимание!
Автор: Дмитрий Брайт