Всем привет! Продолжаем наш цикл статей о внедрении подхода Design API First на проектах нашей компании. Ранее мы рассмотрели использование этого подхода, описали плюсы и минусы, узнали, как на практике выглядит проектирование API на примере сервиса аутентификации. Сегодня расскажем о том, как мы встраиваем Design API First в наш конвейер разработки, подробно остановимся на инструментах, помогающих с технической точки зрения организовать этот процесс. Объясним, как реагировать на изменения требований и обеспечивать версионность, а также что использовать для мокирования данных. Рассмотрим различные варианты применения: для нового проекта, для существующего проекта (где изначально был Code First).

Вместе с коллегами из Архитектурного комитета SimbirSoft мы попытались унифицировать процесс поставки изменений в контрактах, таким образом упростив жизнь командам разработки и ускорив внедрение изменений в проектах с «Непрерывной доставкой и/или развертыванием» (Continuous delivery and Continuous deployment). Для проверки жизнеспособности данного подхода в наших условиях мы используем Design API First для проектирования прикладного слоя сервисов API, предназначенных прежде всего для взаимодействия со слоем Frontend. 

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

В разрезе периодических изменений спецификации необходимо применять эти изменения в обоих мирах. Зачастую реальность устроена так, что владельцем контракта является команда Backend, и изменения, вносимые в спецификацию, сначала применяются в API Backend (подход Code First), а затем поддерживаются на стороне Frontend.

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

В рамках нашей задачи мы инвертируем поток обновления контрактов (относительно Code First) согласно подходу Design API First.

Рисунок 1. Design First вместо Code First
Рисунок 1. Design First вместо Code First

Это позволит командам frontend-разработки получить оперативные изменения спецификаций и приступить к реализации изменений параллельно с командой Backend. 

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

  1. Организационные (например, взаимодействие архитектора, аналитика, разработчика, которые занимаются проектированием спецификации; разработка регламента внесения изменений в спецификацию, уведомление команд о ее готовности и др.).

  2. Структурные (хранение артефактов проектирования в Gitlab, настройка Gitflow, изменения в CI/CD).

  3. Функциональные (поддержка mock-серверов для актуальных версий спецификации, использование ApiGateway в среде разработки с настроенной на эти сервера маршрутизацией).

Для предварительной оценки сложностей во внедрении подхода Design API First в рабочие процессы, а также с целью выявления «подводных камней», мы использовали способ, похожий на «стрельбу трассирующими» из книги «Программист-прагматик. Путь от подмастерья к мастеру» (Дейв Томас и Энди Хант), то есть создали одно простое программное решение по методологии Design API First.

Предварительно мы определили предметную область: для примера взяли файловое хранилище (Рис. 2), описали функциональные и нефункциональные требования (Таблица 1), описали основные элементы этого сервиса (Таблица 2) и протоколы интеграции (Таблица 3), сформировали список первичных тестовых сценариев использования. 

Рисунок 2. Логическая схема работы файлового хранилища
Рисунок 2. Логическая схема работы файлового хранилища

Таблица 1. Требования к реализации системы.

Функциональные требования

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

2. Зарегистрированный пользователь может загрузить файл в хранилище:

– Размер файла не более 1 Мб. 

– Один пользователь не может загрузить более 10 файлов в минуту.

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

4. Зарегистрированный пользователь может изменить свое имя.

5. Зарегистрированный пользователь может посмотреть статистику по своему файлу: количество загрузок, имена пользователей загрузивших файл.

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

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

– Поиск по названию файла и имени владельца. 

– Сортировка по имени файла, имени пользователя, количеству загрузок файла. 

– Пагинация.

7. Любой пользователь может загрузить файл из хранилища. После каждой загрузки счетчик загрузок файла увеличивается на 1. 

Требования по реализации системы

1. Пользователь взаимодействует с системой через единый шлюз доступа по протоколу HTTP.

2. Система состоит из нескольких отдельных сервисов. 

3. У каждого сервиса своя БД.

4. Сервисы взаимодействуют асинхронно, передавая и получая события об изменениях через шину.

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

6. Допускается «согласованность в конечном счете». 

Таблица 2. Описание элементов системы.

Код

Имя

Назначение

G1

Шлюз

Принимает все запросы пользователей.

Выполняет роутинг запросов к сервисам.

Участвует в процессе аутентификации/авторизации.

S1

Сервис авторизации

Авторизация по логину и паролю.

Регистрация новых пользователей.

Изменение профиля пользователя.

B1

Пользователи

Хранение профилей пользователей имеющих доступ к системе.

S2

Сервис загрузки файлов

Загрузка произвольных файлов в хранилище.

Удаление файла из хранилища.

S3

Сервис выгрузки файла

Загрузка файла из хранилища по запросу пользователя.

Ведение истории просмотра файла: кто и когда загружал файл.

B2

Хранилище файлов

Хранение содержимого файла.

Хранение атрибутов файла.

Хранение истории загрузок файла.

S4

Сервис отчетов и поиска

Поиск файлов по атрибутам с сортировкой, фильтрацией и пагинацией.

Генерация отчетов о загруженных файлах.

B4

Отчеты и списки

Хранение отчетов в готовом виде.

Хранение списков файлов со всеми атрибутами в готовом виде.

D1

Шина

Асинхронный обмен данными между сервисами (pub/sub).

Таблица 3. Протоколы интеграции.

Код

Протокол

Описание

U1G1

HTTP/JSON/REST

Все пользовательские запросы.

Имя сервиса указывается в URL запроса (например, http://localhost/files/1).

G1S1

HTTP/JSON/REST

Запрос на аутентификацию по логину и паролю.

Запрос на авторизацию.

Запрос на изменение профиля пользователя.

G1S2

HTTP/JSON/REST

Запрос на загрузку файла в хранилище (POST).

Запрос на удаление файла (DELETE).

G1S3

HTTP/JSON/REST

Запрос на загрузку файла из хранилища (GET).

G1S4

HTTP/JSON/REST

Запрос списка (GET).

Запрос отчета (GET).

S1D1

AMQP/JSON

Сообщение о регистрации нового пользователя.

Сообщение об изменении профиля пользователя.

S2D1

AMQP/JSON

Сообщение о загрузке нового файла.

Сообщение об удалении файла.

S3D1

AMQP/JSON

Сообщение о просмотре файла пользователем.

D1S4

AMQP/JSON

Все сообщения из S1D1, S2D1, S3D1.

Далее мы разработали эталонную спецификацию OpenSpec и создали необходимые диаграммы взаимодействия по примеру тех, которые описаны в нашей предыдущей статье. Следующим шагом было создание структуры хранилища (Рис. 3).

Рисунок 3. Backend для API файлового хранилища
Рисунок 3. Backend для API файлового хранилища

Мы также апробировали инструментальные средства (mock-server, настройку ApiGateway) и проверили техническую готовность нашей инфраструктуры (CI/CD) поддержать итеративную разработку в рамках Design API First. 

Рисунок 4. Поддержка подхода Api Design First на стороне Backend с использованием Imposter (Mock-server) и Ocelot (ApiGateway)
Рисунок 4. Поддержка подхода Api Design First на стороне Backend с использованием Imposter (Mock-server) и Ocelot (ApiGateway)

Учитывая доминирование Code First в практической разработке API среди наших команд и текущих проектов, а также издержки Design First, связанных с ошибками проектирования и необходимостью вносить изменения в контракт на этапах написания кода, было целесообразно сразу отработать два сценария:

  1. Разработка проекта API идет в парадигме Code First, поэтому мы хотим прибегнуть к подходу Design API First, применяя его для проекта или его части.

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

Не дожидаясь завершения этапа проектирования, команда Backend написала программное API, руководствуясь первоначальными требованиями в части работы с файлами по традиционному подходу Code First (сценарий 1). Далее взяли спецификацию API для авторизации (описанную в предыдущей статье) и реализовали эту часть программного API согласно спецификации.

Рисунок 5. Разработка новой версии API управления пользователями на Backend
Рисунок 5. Разработка новой версии API управления пользователями на Backend

После этого мы инициировали необходимость изменения спецификации API по работе с файлами (сценарий 2), провели еще одну итерацию проектирования и разработки, тем самым проверяя готовность команд (аналитики, разработчики, архитекторы) на взаимодействие в ключе подхода Design API First в условиях перехода с Code First или необходимости внести изменения в текущую версию API на поздних этапах (при написании кода согласно полученным артефактам проектирования).

Рисунок 6. Внесение изменений в спецификацию API на этапе реализаций в Backend
Рисунок 6. Внесение изменений в спецификацию API на этапе реализаций в Backend

Заключение

Таким образом мы получили систему, которая удовлетворяет всем требованиям, описанным выше, и позволяет продолжать разработку в режиме Design API First.

В наших следующих статьях мы расскажем, как указанные сценарии выше были отработаны командой проектирования API. А также какие изменения потребовалось внести в код проекта Backend для перехода с Code First на Design API First. Небольшой спойлер :) Часть Backend написана на .Net 6, а для генерации моделей мы использовали кодогенераторы Roslyn совместно с NSwag.

Комментарии (0)