? На самом деле, моя идея написания тестов на архитектуру настолько проста, легко реализуема и при этом полезна, что я до сих пор толком не понимаю, почему я не встречал материалов на эту тему, и сама тема всё ещё не используется повсеместно ?
Статья написана по следам моих докладов на трёх крупных ИТ-конференциях, на каждой из которых ко мне подходили архитекторы и разработчики российских бигтехов, говорили, что я очень точно попал в их боли и предложил суперпрактику, которую они теперь будут внедрять. На всех трёх конференциях я получил высшие оценки от аудитории, а на двух из них доклад был признан лучшим в своей секции. В конце статьи приведена ссылка на видео доклада с одной из конференций.
В статье я поделюсь своей идеей и OpenSource-реализацией решения для написания тестов, разберу примеры тестов на небольшой учебной микросервисной архитектуре, а также расскажу про личный опыт и профит от применения этой практики.
Для разработчиков монолита тоже есть небольшой бонус: в OpenSource-репозитории появилась реализация и примеры тестов на архитектуру модульного монолита.

Архитектура as Code

Прежде чем говорить об архитектуре as Code, давайте в целом попробуем дать (а точнее, выбрать из множества) определение ИТ-архитекту��ы.

Возьмём определение, приведённое в стандарте ANSI/IEEE Std 1471-2000:

Фундаментальная организация системы, реализованная в её компонентах, их взаимоотношениях друг с другом и средой и принципах, определяющих её конструкцию и развитие

Отдельно в этом определении хочется выделить, что архитектура — это не только «организация системы», но и «принципы, определяющие её конструкцию и развитие».

То есть архитектура — это не только «что», но и «почему». Это важно.

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

У визуализированной в виде картинки архитектуры есть один несомненный и самый главный плюс — она наглядна!

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

Но у картинок есть и ряд недостатков: с архитектурой, представленной в виде картинки, сложно работать по привычному разработчикам процессу работы с кодом в системе контроля версий. Да, можно хранить разные версии картинок, но как наглядно показать diff? Как понять, что изменилось, как организовать ревью PullRequest’ов, сравнение между ветками и т. д.? А если пойти дальше — как синхронизировать картинку с исполняемым кодом или даже проинтегрироваться с ней ради автоматизаций?

В противовес условной картинке у кода есть все эти возможности, но есть главный недостаток — отсутствие наглядности.

Итак, идея формата схем as Code очень проста — объединяем все плюсы картинки и кода, избавляясь от всех недостатков :) По сути, нам нужно описать схему кодом, по которому можно однозначно сгенерировать визуализацию. Как это выглядит, можно посмотреть на гифке: слева мы печатаем код, по которому справа тут же онлайн генерируется схема:

На гифке представлен пример на PlantUml (точнее, на PlantUml с подключённым C4 форматом архитектур), на нём же мы и рассмотрим сегодня примеры. Инструментов для описания различных схем кодом уже достаточно много, из открытых могу посоветовать ещё, к примеру, Structurizr.

? Не стоит пугаться незнакомых инструментов, форматов и кучи ссылок — по ним вовсе не обязательно переходить для дальнейшего чтения и понимания статьи ?

Для описания микросервисной архитектуры нам потребуется всего три элемента:

  • System_Ext — внешняя для нашего проекта система, с которой мы проинтегрированы.

  • Container — собственно микросервис (или докер-контейнер с микросервисом внутри).

  • Rel — связь чего угодно с чем угодно (первых двух пунктов выше в любой комбинации).

На PlantUml это выглядит примерно так:

System_Ext(goods, “Goods Repo")
System_Ext(stock, “Stock")

Container(bff, “BFF", "NestJS")
Container(task_repository, “Task Repo", "NestJS”)

Rel(bff, goods, "Get products", “API Gateway")
Rel(task_repository, stock, “Send task", $tags="async")
Rel(bff, task_repository, "Get task", “REST")

Проблемы архитектурных схем

Прежде чем перейти к тестам, давайте сформулируем проблемы, которые хотим решить, иначе зачем это всё?! ?

Сталкиваясь как ИТ-архитектор с множеством проектов различных компаний, я выделил три основные широко распространённые и ощутимые проблемы архитектурных схем:

  1. Неактуальность.

  2. Декларативность.

  3. Отсутствие контроля над исполнением.

Неактуальность

Архитектурная схема — это та же документация. А кто не встречался с проблемой неактуальной документации? ?

Бывает, смотришь на ИТ-архитектуру, там всё чётко, стройно и красиво. А на самом деле на проде… ну, не совсем так:

Знакомо? ?

Бывает и чуть иная ситуация:

Актуальна ли здесь архитектурная схема? Ну, может, и актуальна, только есть ли в этом смысл? ? Неуверенность или незнание статуса актуальности приводит к тому, что даже наличие этой самой актуальности не приносит пользы.

Декларативность

Под декларативностью на архитектурных схемах я понимаю как раз отсутствие на ней принципов, по которой она спроектирована. То есть схема как раз и показывает, «что» сделано, не отвечая на вопрос «Почему именно так?» Давайте покажу на простеньком примере:

Микросервис бизнес-логики обращается к микросервису-репозиторию, который идёт в БД за данными. Схема показывает, как сейчас взаимодействуют между собой элементы системы, но ничего не говорит, например, о том, можно ли из бизнес-логики напрямую ходить в БД?

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

Отсутствие контроля над исполнением

Ладно, пусть мы где-то в документации рядом с архитектурной схемой или даже просто на словах договорились, что напрямую к БД ходят только их микросервисы-репозитории. Но если кто-то из разработчиков по незнанию или по недобросовестности возьмёт и реализует в коде прямой запрос к БД из стороннего микросервиса, когда об этом узнает архитектор? Есть вероятность, что слишком поздно, когда поверх этого будет накоплен уже большой техдолг. Ревью кода, конечно, может спасти, но и оно не гарантирует стопроцентной защиты от нарушения договорённостей.

Итак, у нас есть проблемы с архитектурой, которую мы теперь можем описывать в коде (as Code), а значит, у нас есть проблемы в коде!

А если у нас есть проблема в коде — надо написать тест!

Покрытие архитектуры тестами

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

Для наглядности возьмём микросервисную архитектуру, в которой есть внутренние (между контейнерами) и внешние (интеграции со сторонними системами, обозначенными серым цветом) взаимодействия. Взаимодействия (или зависимости) есть прямые (например, REST) и асинхронные (например, подключения к Kafka). Это всё, что стоит знать про наш пример архитектуры, подробно её разбирать нам не потребуется:

Напомню, что архитектура у нас описана в PlantUml при помощи трёх элементов:

System_Ext(goods, “Goods Repo")
System_Ext(stock, “Stock")

Container(bff, “BFF", "NestJS")
Container(task_repository, “Task Repo", "NestJS”)

Rel(bff, goods, "Get products", “API Gateway")
Rel(task_repository, stock, “Send task", $tags="async")
Rel(bff, task_repository, "Get task", “REST")

Откуда брать данные?

Итак, чтобы проверить актуальность нашей архитектурной схемы, нам нужно понять, с чем её сравнивать, где взять реальные взаимодействия микросервисов?

ServiceMesh

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

Если снимать трафик с прода, во временное окно выгрузки данных должны будут попасть все связи, включая «редкие». Под «редкими» я подразумеваю зависимости микросервисов, которые используются, например, раз в месяц при генерации какого-нибудь отчёта. Если снимать с препрода, нам нужно на нём интеграционными/end-to-end тестами симулировать все сценарии и потоки данных, что уже само по себе является нетривиальной задачей. Отдельный вопрос, как при этом поддерживать актуальность и соответствие нашей симуляции реальному положению дел на проде ?

Также в обоих вариантах мы получаем достаточно долгую обратную связь. Чтобы по трафику получить граф взаимосвязей, с которым мы сравним архитектуру для проверки актуальности, нам понадобится как минимум прогнать все интеграционные тесты, а лучше дождаться выкладки новой версии на прод и исполнения на проде всех сценариев, включая «редкие». А мы-то хотели аналогичный работе с кодом процесс, чтобы хотя бы на этапе PullRequest’а в develop тесты автоматом говорили, всё ок или не ок ?

А если не ServiceMesh, то что?

IaC

Вспомним, что у нас Infrastructure as Code уже практически является стандартом в отрасли, теперь у нас появляется и Architecture as Code, так почему бы два этих подхода не поженить?!

Если взять конфигурации микросервисов (их контейнеров) для kubernetes, получим сразу всё необходимое для нашей задачи практически без каких-то трудностей.

В таком конфиге мы явно прописываем все возможные взаимодействия микросервиса: исходящие REST-запросы, доступ к очередям и базам данных. Что касается скорости обратной связи: если я как разработчик добавляю новый REST-вызов из сервиса A к сервису Z, то я пропишу в конфиге микросервиса доступ A→Z на уровне инфраструктуры, чтобы проверить работоспособность кода на feature-окружении. То есть мы получаем обратную связь и можем запустить тест на архитектуру уже на этапе развёртывания задачи в feature-окружении, до какого-либо слияния веток и ревью PullRequest’а.

Для деплоя мы используем helm-чарты, для которых конфигурация каждого микросервиса лежит в yaml-файлах. Приведу упрощённый пример такой конфигурации, который содержит всё необходимое для проверки архитектурных связей этого микросервиса:

environment:
  GOODS_REPO_BASE_URL:
    default: https://gateway.int.com:443/goods/v1
  TASK_REPO_BASE_URL:
    default: http://tasks-repository:8080
  KAFKA_TASKS_BROKERS:
    default: msg-q8s.int.cloud:9092
  KAFKA_TASKS_TOPIC:
    default: stock-tasks-q8s-v1

Конфиг говорит, что наш микросервис шлёт REST-запросы к внешней системе (Goods_Repo) и к другому микросервису внутри нашего периметра (Task_Repo), а также подписан на топик кафки.

Тесты на актуальность

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

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

Для построения таких массивов нам понадобятся два парсера исходных источников данных и общий маппер приведения к единой структуре данных, по которой мы уже сможем писать обычные тесты (unit-тесты):

Код реализации, а также пример архитектуры, инфраструктуры и тестов можно посмотреть и взять себе на вооружение в нашем опенсорсе: https://github.com/Byndyusoft/aact Welcome! =)

? Далее по тексту статьи я буду приводить прямые ссылки на конкретные примеры в этом репозитории.

Простой тест на сопоставление списков микросервисов может выглядеть примерно так: 

it("finds diff in configs and uml containers", () => {
  const namesFromDeploy = deployConfigs.map((x) => x.name);
  const containerNamesFromPuml = containersFromPuml
    .filter((x) => x.type == "Container")
    .map((x) => x.name);

   expect(namesFromDeploy).toEqual(containerNamesFromPuml);
});

github.com/byndyusoft/aact/blob/ac05e3217ac4bc8d621ebbd1323ac9c86a828956/test/architecture.test.ts#L43

Берём имена всех микросервисов из конфигов (namesFromDeploy) и из архитектуры (containerNamesFromPuml), отфильтровав при этом внешние системы. Внешние системы нас будут интересовать только в плане наличия связей нашего периметра с ними, их конфигурационных файлов у нас нет, да и не должно быть.

На моём проекте даже тест на простое сопоставление имён микросервисов уже выявил ряд несовпадений — от расхождений в именовании до откровенно лишних забытых микросервисах и на архитектуре, и в конфигурациях.

Для проверки актуально��ти архитектурной схемы помимо списка имён как минимум нужно проверить все связи каждого из микросервисов. При первом запуске такого теста на достаточно развесистой архитектуре, как правило, выявляются как недостающие, так и лишние связи. Пример output’а такого теста:

Container name bff ✅
Container name task_repository❌
Container name invoice_acl ✅
 --------------------------------------------------------
First failed container name task_repository
--------------------------------------------------------
FAIL test/architecture.spec.ts
● Architecture › finds diff in configs and uml dependencies
expect(received).toEqual(expected) // deep equality
- Expected - 1
+ Received + 1
Array [
“bff",
- “stock",
+ “goods",
“invoice", 

github.com/byndyusoft/aact/actions/runs/6603088421/job/17935846325#step:5:67

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

  • Типы и параметры связей — протоколы взаимодействия, указание конкретных REST API и очередей, настройки прав доступа и т. д.

  • Параметры контейнеров — количество реплик, параметры CPU и RAM, настройки autoscaling и т. д.

  • Форматирование и конвенции именования — файлов конфигураций и архитектур, именование переменных окружения и т. д.

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

Тесты на соблюдение архитектурных принципов

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

1. ACL (Anti-corruption Layer) Pattern

Суть принципа: за интеграцию с внешними системами ответственны отдельные микросервисы-адаптеры, инкапсулирующие в себе знания о внешних системах (например, об их доменных сущностях).

Рассмотрим на примере нашей учебной архитектуры, где ✅ помечены валидные связи (принцип соблюдён); ❌ помечены связи, нарушающие принцип (так как внешняя интеграция идёт напрямую из внутреннего микросервиса, не являющегося адаптером (не стоящего на периметре)); сами микросервисы-адаптеры выделены зелёным.

Чтобы написать тест, на архитектуре в PlantUml помечаем соответствующие микросервисы признаком Adapter:

Container(goods_adapter, “Goods ACL", "NestJS", $tags="adapter")

В тесте проверяем, что связи с внешними системами имеют только сервисы с таким признаком. Помним, что для внешних систем в PlantUml используем отдельный элемент System_Ext (визуально он влияет только на заливку такого элемента серым цветом).

Таким образом, если на архитектуре у нас будет представленная выше связь от контейнера Camunda, не помеченного тегом adapter, к внешней системе Stock: 

System_Ext(stock, "Stock")
Container(camunda, "Camunda")
Rel(camunda, stock, "")

то тест на ACL принцип упадёт.

2. Пассивные репозитории

Суть принципа: микросервисы, отвечающие за доступ к мастер-данным, не должны обладать дополнительной логикой и иметь зависимостей, помимо своей БД.

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

Для возможности тестирования применяем всё тот же подход с тегом: помечаем микросервисы-репозитории (их ещё иногда называют CRUD API) спецпризнаком:

Container(task_repository, “Task Repository", "NestJS", $tags="repository")

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

Rel(task_repository, task_db, "")
Rel(camunda, task_repository, "")
Rel(task_repository, invoice_acl, "")

3. Внешние REST-вызовы должны идти через API-Gateway

Договорённость говорит о том, что внутри периметра мы ходим между микросервисами (контейнерами) напрямую через service кубера, а вовне периметра — через отдельно развёрнутый API Gateway. В конфигах инфраструктуры это можно отследить по формату URL:

GOODS_REPO_BASE_URL:
  default: https://gateway.int.com:443/goods/v1
TASK_REPO_BASE_URL:
  default: http://tasks-repository:8080

Раз нам важны URL для REST-интеграций, то отобразим их и на архитектуре подписью к связям:

Rel(goods_acl, goods_repository, "", "https://gateway.int.com:443/goods/v1")

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

В тесте на использование API Gateway проверяем все исходящие связи к внешним системам, например, на соответствие формату URL: https://gateway.int.com:443/*. Такой тест в нашем примере упадёт на строке:

Rel(stock_acl, stock, "", "http://stock:8080")

так как элемент stock является внешней системой (System_Ext), а значение параметра "http://stock:8080" не соответствует паттерну URL API Gateway’я.

4. Операции записи идут только через оркестратор бизнес-процессов

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

В нашем примере оркестратором распределённых транзакций является Camunda:

Такое разделение на уровне инфраструктуры приведёт к тому, что в конфигурации права только на чтение и на чтение и запись будут различаться (например, по X-API-Key или иным другим способом). Также уровень доступа у связи разделим и на архитектуре знакомыми нам тегами, только теперь это будут атрибуты не элементов, а собственно связи:

Rel(invoice_acl, invoice_repository, "", $tags="rw")

Вновь не забываем добавить проверку на соответствие атрибутов прав записи в тесты на актуальность. В тесте на соблюдение правил обработки распределённой транзакции провер��ем, что тегом rw к репозиториям обладают только исходящие из Camunda (микросервиса с тегом оркестратора) связи.

И другие принципы

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

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

  • Разделение сервисов по хостам и дата-центрам.
    Часто на архитектуре важно отразить распределение системы по «железу» и по хостингу в том или ином дата-центре. Так как данные для сверки мы черпаем из инфраструктуры as Code, проверить на актуальность не составит труда. Помимо актуальности можно проверить и выполнение различных правил, к примеру, что инстансы критичных микросервисов запущены на разных хостах или дата-центрах.

  • Наличие DLQ (dead letter queue) для критичных потоков асинхронных данных. Для проверки этого правила также всё у нас есть ?

  • И так далее…

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

В целом теперь можно и нужно поступать с архитектурными проблемами так, как привыкли поступать с багами разработчики: нашёл проблему — напиши тест! Покрыв таким образом архитектурный техдолг тестами, мы получаем метрику величины техдолга в виде количества упавших тестов. Или даже более детальную метрику: тест покажет, что конкретный принцип проектирования нарушен, к примеру, в четырёх микросервисах. Даже если сразу же нет возможности исправить архитектурный техдолг, всё равно советую не скипать соответствующие тесты, а проверять в них, что техдолг не вырос. К примеру, в тесте проверять, что количество нарушающих принцип микросервисов строго равно четырём. При таком подходе, если вдруг появится пятый микросервис, тест упадёт и не даст нарастить техдолг. Тест также упадёт, если мы займёмся рефакторингом архитектуры и исправим часть техдолга, но это будет радостный момент, и мы заменим в тесте четвёрку на тройку.

Решение архитектурных проблем

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

Актуальность

Архитектурная схема поддерживается в актуальном состоянии из-за автоматического прогона тестов при сборке очередной версии на CI/CD. Если разработчик добавляет новую связь между микросервисами, без добавления в конфиг она физически нигде не заработает (не будет доступно сетевое взаимодействие), а при добавлении в конфиг все связи автоматически свалидируются с архитектурной схемой. То есть в случае несоответствия схеме очередной билд попросту не соберётся, да и авто��атические проверки при создании PullRequest’а также не пройдут из-за упавшего теста.

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

Тесты описывают императивность архитектуры

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

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

Тесты описывают и фиксируют принципы и договорённости проектирования и автоматически проверяют схему на соответствие им.

Контроль изменений в архитектуре

Проверка на актуальность и соответствие в тестах отсеет случайные ошибки или нарушения архитектуры по незнанию. Если же разработчик внесёт изменения в архитектуру и/или в сами тесты, здесь поможет обязательное требование апрува от архитектора (аккаунта, состоящего в группе архитекторов) для слияния PullRequest’а в архитектурном репозитории.

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

Личный опыт

На рисёрч, разработку и внедрение идеи с нуля на первом проекте в сумме у нас ушло около трёх человеко-недель. На последующих проектах, если полностью совпадали по стеку, для внедрения требовалось только взять готовый код и примеры тестов из OpenSource, без дополнительных трудозатрат. На некоторых проектах отличалась инфраструктура или форматы и подходы к её хранению as Code, в таких случаях командам требовалось лишь добавить свою прослойку для парсинга инфраструктуры, сохранив при этом всю остальную структуру и код из примера:

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

Не знаю, как вам, а мне доставляет отдельное удовольствие исправлять техдолг, удаляя неиспользуемый код :) Вдвойне приятно, когда после этого ещё и тест из красного становится зелёным ?

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

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

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

Что дальше?

Советую и вам покрывать архитектуру ваших проектов тестами, возможно, и вашей команде это даст даже больше пользы, чем вы изначально будете ожидать ?

Буду рад новым контрибьюторам в opensource-репозиторий с кодом и примерами архитектурных тестов, да и просто звёздочкам на гитхабе. В ближайшее время надеюсь доделать и опубликовать roadmap развития проекта, и мне очень интересны примеры ваших архитектур и принципов — закидывайте в репозиторий (можно в Issues), даже если не знаете, как протестировать тот или иной принцип или договорённость, будем вместе думать и развивать количество покрытых тестами примеров паттернов проектирования!
Ещё раз ссылка на публичный репозиторий: https://github.com/Byndyusoft/aact.
Репозиторий также доступен и на российской площадке: https://gitverse.ru/Byndyusoft/aact, репозитории синхронизированы.

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

Далее можно будет порассуждать и об обратной генерации и её целесообразности — инфраструктуры по архитектурной схеме. А если уж заглядывать совсем далеко — о генерации и архитектуры, и инфраструктуры из первичного замысла проектировщика, из его идей, архитектурных решений, внешних факторов и требований к проекту ?

Кого заинтриговал, подписывайтесь на мой канал: Архитектура распределённых систем.

Материал этой статьи в формате доклада:

И пара слов обо мне:

Руслан Сафин

Соучредитель, технический директор и архитектор в «Бындюсофт».
Автор и преподаватель курса по микросервисной архитектуре в ИТМО.
Член программных комитетов конференций CodeFest и TechLeadConf.

❤️