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

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

На уровне кода границы выделяются через файловую структуру, на уровне системы через независимые пакеты развертывания.

Границы необходимы для нескольких целей:

Границы нужны чтобы их пересекать, для этого вводятся правила (интерфейсы) взаимодействия. На уровне кода - это способность через интерфейс обратиться к функциям за пределами границы. Границы который нельзя пересекать - это демилитаризованные зоны, пересечение таких границ делается по "шлюзовому" принципу - сторона А положила данные в шлюз, заблокировала шлюз, сторона Б открыла шлюз со своей стороны и забрала данные.

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

Разработка

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

Развертыване

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

Сопровождение

Раздельная разработка позволяет организовать и раздельное сопровождение, созданных компонент.

Модернизация

Изоляция позволяет полностью переписать один компонент, заменив его новой версией.

Кроме очевидных достоинства, у разделения приложения на части, т.е. проведения полноценных границ, имеется существенный недостаток - увеличение накладных расходов:

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

В книге "Чистая архитектура" Роберт Мартин предлагает сделать все работы по созданию границ на уровне кода:

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

В любой программе есть поток данных, которых как минимум два:

Наличие популярных потоков данных отражено в MVC архитектуре, где M - приближена к хранению данных, а V - приближена к пользователю. Данные потоки являются так же архитектурными границами, только их гораздо удобнее представлять в виде вертикальной иерархии, где снизу располагаются компоненты имеющие отношение к хранению данных, а сверху компоненты, имеющие отношение к вводу данных.

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

Durtion: 5

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

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

Организация сервисного подхода позволяет проводить границы на уровне системы, а не на уровне кода, что проще для восприятия и управления уровнем сложности.

Нужно отметить, что у Роберта Мартина в Чистой архитектуре сервисный подход рассматривается только через призму служб и при этом разбиение системы на сервисы он предлагает считать архитектурой только когда это разделяет высокоуровневые политики от низкоуровневых деталей. А сам факт обращения к функциям сервиса считать "дорогим" вызовом обычной функции, если нет высокоуровневой абстракции. Это лишено всякого смысла, скорее всего тут должна быть какая-то "высокая" мысль. Но на самом деле ее нет.

В целом все рассуждение о службах и сервсиах упирается в одну ошибку - он забыл про "версии". Если научиться пользоваться версионированием данных и протоколов, то проблема независимости просто не стоит.

Любое сложное приложение можно декомпозировать на части большим количеством разных способов. Поэтому проведение границ - это процесс поиска оптимального решения, удовлетворяющего текущим требованиям и веторам развития.

Но даже при небольшом количестве требований, проектирование от границ требует существенной дополнительной нагрузки, которая требует:

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

Далее отталкиваясь от того что хочет видеть пользователь, можно построить решение по следующему принципу:

Проектирование от интерфейса позволяет быстро делать небольшие монолиты. Точность такого проектирования не очень высокая, но позволяет быстро сделать MVP.

Для вэб-приложений, которые изначально планируется делать на REST архитектуре, можно изначально разделить разработку клиента, на основе интерфейса и построения серверной части на основе API.

Для такого подхода необходимо:

Такой вариант очень прост, так по пусти мы сначала определяем как хотим получать и обрабатывать данные, потом реализуем в варианте "один к одному". Разработчикам проще размышлять категориями запроса, бизенс-задачи максимально близко отражаются в API.