Архитектура приложений

Принципы архитектуры

Корпоративная архитектура заключается в проектировании слабосвязанных, повторно используемых, автоматизированно тестируемых сервисов и ядер.

Проектируя микросервисы, мы хотим уйти от сложности монолита. Однако без соблюдения границ приложений и предметных областей, с синхронным взаимодействие компонентов в результате получается худшая форма монолита — микросервисный монолит:

Назначение данного документа — помочь найти решение данных проблем. Архитектура программного обеспечения сильно отличается от архитектуры зданий. Этот процесс больше похож на творчество, и понимание ошибок прошлого здесь критически важно. Однако даже эти знания не гарантируют результата.

Микросевисы

Разделение функций

  1. Каждый отдельно взятый компонент или сервис должен отвечать только за одну конкретную бизнес-функцию

  2. Компонент или сервису не должны быть известны детали других компонентов или сервисов.

Группирование сервисов

Сервисы, использующие общую предметную область, группируются в модули.

Иерархия сервисов

Модель сервисов плоская, однако в случаях, когда предметная область одного сервиса является подмножеством предметной области другого, можно говорить о «дочернем» сервисе.

Повторное использование сервисов

Для обеспечения повторного использования сервисов, зависимости сервисов
передаются динамически через HATEOAS (Hypermedia as the Engine of Application State).

Типы зависимостей

Зависимые сервисы в зависимости от выбранной архитектуры могут предоставлять программный интерфейс либо готовое отображение.

Допустимо использование низкоуровневых, повторно используемых зависимостей, предоставляющих простой программный интерфейс и простую, не "плывущую" структуру данных, например для обмена структурированной файловой информацией. Данная структура не завязывается на конкретное приложение и является транзитной.

В остальных случаях, наиболее простой способ подключения зависимостей - виде готовых визуальных компонентов, подключаемых в iframe.

Типы взаимодействия

При проектирования слабо связанных сервисов существует два типа взаимодействия

  1. Синхронное взаимодействие

  2. Асинхронное взаимодействие

Предпочтение отдается асинхронному взаимодействию (получающая сторона запрашивает данные при необходимости)

Синхронное взаимодействие

Недостатки синхронного взаимодействия:

  1. Отказ сервиса приводит к отказу всей цепочки. Задержка сервиса приводит к задержке всей цепочки.

  2. Смешение предметных областей сервисов. Один слабосвязанный сервис знает о наличии другого. Продлевая эту цепочку мы в худшем случая получаем бесконечную зависимость одного сервиса от всей инфраструктуры организации.

Достоинства синхронного взаимодействия

  1. Простота реализации. Решение задачи «в лоб». Вы добавляете зависимость, добавляете вызов, а в ад микросервисного монолита попадаете потом.

Прямое синхронное взаимодействие разумно предпочитать в вызове дочерних сервисов, но с вызовами прочих зависимостей его следует «декорировать» одним или несколькими способами

  1. Создание «шлюзов» вызова сервисов

  2. Использование HATEAOS ссылок с «готовыми» параметрами других предметных областей.

Пример:

  1. В рамках проверки залога вам требуется вызов сервиса внешнего шлюза, куда требуется передать «код заявки».

  2. В рамках проверки залога вам требуется вывести информацию о заемщике из заявки.

Проблема:

Проверка залога — слабосвязанный сервис и добавив в него зависимость от заявки вы перечеркнете все то, ради чего все он создавался — повторную используемость сервиса, в том числе и без заявки. В данном случае полезна аналогия «нельзя быть немного беременной» - нельзя быть немного связанным с заявкой. Если единственная зависимость не позволяет использовать модуль повторно, цели вы не достигнете.

Решение:

Слой бизнес-процессов внедряет ссылку вызова шлюза в виде HATEAOS зависимости, с уже подставленным кодом заявки:

  1. Сервис получения информации из шлюза в формате PDF: https://link.to.service.php?applicationUid=<uuid заявки>&vin=${vin}. Переменная VIN, в отличии от кода заявки, является частью предметной области сервиса проверки залога, и следовательно может быть легко им подставлена.

  2. Сервис отображение информации о заемщике в формате xhtml: https://link.to.service2?applicationUid=<uuid заявки>, внедряемый с помощью iframe.

Суть этих двух решений - в переходе от передачи «плывущих» структур данных в форматах xml или json, вносящих зависимости от смежных предметных областей, на использование готовых сервисов с интерфейсом пользователя, к решениями, изначально заложенным в web 25 лет назад.

Асинхронное взаимодействие

Недостатки асинхронного взаимодействия

  1. Требуется продумывать обработку ошибок, способ реагирования, устранения ошибок, реакции на процесс в целом.

  2. Требуется координатор, отслеживающий запуск асинхронных сервисов

Достоинства асинхронного взаимодействия

  1. Отказ или временная задержка сервиса не приводит к отказу всей цепочки «здесь и сейчас».

  2. Нет смешения предметных областей различных сервисов.

Ядро

Построение цельной архитектуры не возможно только на слабосвязанных сервисах. Мы можем выносить функционал из ядра системы, но мы не можем убрать ядро системы, не усложнив при этом взаимодействие модулей до бесконечности. Это следует из того, что вызов сервисов намного дороже и сложнее вызова методов текущего процесса. Кроме того, само проектирование асинхронной слабосвязанной архитектуры объективно сложнее.

Поэтому предлагаемую архитектуру следует считать «гибридной», состоящей из ядер и микросервисов.

Под термином «ядро» понимается «модульный монолит». «Модульный» монолит означает модульность на этапе сборки приложения. Подключая разные зависимости к ядру, мы можем собрать различные варианты приложения, в том числе для разных экземпляров приложения.

Корпоративная среда может содержать несколько «модульных» ядер. Примеры:

Слои архитектуры

Слой бизнес-процессов

Слой бизнес-процессов знает окружающую инфраструктуру, оперирует указателями ресурсов LDAP. Управляет логикой бизнес-процесса "верхнего уровня". Например знает о том, что когда запустить то или иной сервис, откуда взять данные и как их преобразовать в предметную область сервиса.

Слой слабосвязанных сервисов

Сервисы оперируют только собственной предметной областью. Контекст вызова сервиса передается из слоя бизнес-процессов в виде HATEOAS ресурсов (своего рода аналог dependency injection веба).

Сервис не знает предметной области окружающей инфраструктуры и не оперирует ссылками вышестоящих ресурсов (ничего не знает о том, что над ним). Таким образом сервис становится слабосвязанным и повторно используемым.

Пример взаимодействия

Бережливое тестирование

Использование модели dependency injection на уровне web с помощью HATEAOS позволяет проводить «бережливое» (эффективное по ROI) интеграционное тестирование сервисов, так как позволяет использовать обычные файлы в качестве «контекста». Вы просто готовите контекст вызова на файлах и передаете его ссылку в contextUrl сервиса.

Принципы проектирования

  1. Self-Contained Systems Assembling Software from Independent Systems

  2. Процесс разработки программного обеспечения ICONIX

  3. Проектирование на основе предметной области (Domain Driven Design, DDD)

  4. Разработка через тестирование (Test Driven Design, TDD)

  5. REST API — Что такое HATEOAS?