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

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

Идея многоуровневой архитектуры основана на идее программирования интерфейсов. Когда один модуль взаимодействует с другим через интерфейс, вы можете заменить одного поставщика услуг другим [2].

Не волнуйся! Я постараюсь объяснить просто.

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

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

Каковы роли этих слоев (обработчик, сервис и репозиторий)❓

Handler: это уровень, который получает http-запрос и возвращает http-ответ клиенту.

Сервис. Это слой, на котором находится наша бизнес-логика.

Репозиторий: это уровень, который предоставляет все необходимые данные из внешнего (базы данных) или внутреннего (в памяти) источника данных. Для простоты мы используем in-memory.

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

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

Не волнуйтесь, вы поймете лучше, когда мы увидим действие:

На самом деле, когда мы вызываем метод службы в Handler и метод репозитория в Service, разумно думать об этих отношениях как о «стеке вызовов».

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

Давайте представим это воспоминание в виде коробки.

Наш первый вызов (в Handler) нужно сохранить в памяти.

Затем наш второй вызов (в Service) сохраняется в памяти в поле Handler.

Затем наш третий вызов (в Repository) сохраняется в памяти в поле Service. Сложность здесь в том, что нашему сервису нужно дождаться выполнения функции репозитория. Когда это будет сделано, наш обработчик должен дождаться выполнения сервисной функции. Когда это будет сделано, функция Handler может выполнять свою работу.

После того, как это тоже будет сделано, наш стек будет пуст. Вот как работает стек вызовов. Вы можете прочитать раздел Стек вызовов в Книге алгоритмов Grokking, чтобы узнать больше.

Это широкая перспектива того, как создается наш дизайн. Мы можем углубиться в наш проект. Мы рассмотрим только пример запроса PATCH вместе с этой статьей.

1. главная.идти

Когда клиент отправляет запрос с помощью метода PATCH, метод UpdateMovie в movieHandler вызывается с использованием httprouter.

2а. Обработчик

Это первый уровень, на котором наш API получает HTTP-запрос.

Обработчик — это слой, который преобразует ответ, поступающий от службы, в ответ http. Как вы знаете, ответ HTTP состоит из кода состояния, заголовка и тела.

2б. Тестирование обработчика

Прежде чем продолжить тестирование обработчика, хочу представить mockgen package. Этот пакет помогает нашему коду легко тестироваться.

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

mockgen -source service/movie_service_interface.go -destination service/mock_movie_service.go -package service

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

3а. Услуга

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

3б. Тестирование услуг

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

mockgen -source repository/movie_repository_interface.go -destination repository/mock_movie_repository.go -package repository

Я дам вам трюк. Всякий раз, когда мы меняем интерфейсы, которые мы даем mockgen, нам нужно снова запускать команду mockgen.

До сих пор у нас было два фиктивных файла, и нам приходилось писать эти длинные коды снова и снова, когда мы добавляем новый метод в наш интерфейс. Такой утомительный способ! Решение Makefile. С Makefile мы используем только команду generate-mocks для воссоздания наших фиктивных файлов. (make generate-mocks)

Мы тестируем возможные ошибки, издеваясь над репозиторием.

4. Репозиторий

Здесь реализована наша интеграция данных:

Бонус: плагин HTTP-клиента

Мы пытаемся разработать RESTful API, и в процессе разработки мы хотим убедиться, что он работает должным образом. Чтобы быть уверенным, нам нужно отправлять HTTP-запросы к нашему API.

Вы можете сделать это обоими способами, используя curl, Postman, Insomnia и плагин Jetbrains HTTP Client и т. д. Мне очень нравится использовать плагин Jetbrains HTTP Client.

Подключаемый модуль HTTP Client позволяет создавать, редактировать и выполнять HTTP-запросы непосредственно в редакторе кода IntelliJ IDEA.

Исходный код



Рекомендации

[1,2] Чистая архитектура. Руководство мастера по структуре и дизайну программного обеспечения (Роберт С. Мартин)

[3] 97 вещей, которые должен знать каждый программист, коллективная мудрость от экспертов Кевлин Хенни