В сервисах объявлений есть большое количество неоднородного контента, для которого необходимо иметь единую систему управления. В частности, необходимо показывать разный контент пользователям в зависимости от их принадлежности к какой-либо группе. Данный контент будет предоставляться с помощью баннеров.
Необходимо реализовать сервис, который позволяет показывать пользователям баннеры, в зависимости от требуемой фичи и тега пользователя, а также управлять баннерами и связанными с ними тегами и фичами.
Баннер — это документ, описывающий какой-либо элемент пользовательского интерфейса. Технически баннер представляет собой JSON-документ неопределенной структуры. Тег — это сущность для обозначения группы пользователей; представляет собой число (ID тега). Фича — это домен или функциональность; представляет собой число (ID фичи).
- Один баннер может быть связан только с одной фичей и несколькими тегами
- При этом один тег, как и одна фича, могут принадлежать разным баннерам одновременно
- Фича и тег однозначно определяют баннер
Так как баннеры являются для пользователя вспомогательным функционалом, допускается, если пользователь в течение короткого срока будет получать устаревшую информацию. При этом существует часть пользователей (порядка 10%), которым обязательно получать самую актуальную информацию. Для таких пользователей нужно предусмотреть механизм получения информации напрямую из БД.
- Используется этот API.
- Тегов и фичей небольшое количество (до 1000), RPS — 1k, SLI времени ответа — 50 мс, SLI успешности ответа — 99.99%.
- Для авторизации доступов должны использоваться 2 вида токенов: пользовательский и админский. Получение баннера может происходить с помощью пользовательского или админского токена, а все остальные действия могут выполняться только с помощью админского токена.
- Реализовать интеграционный или E2E-тест на сценарий получения баннера.
- Если при получении баннера передан флаг use_last_revision, необходимо отдавать самую актуальную информацию. В ином случае допускается передача информации, которая была актуальна 5 минут назад.
- Баннеры могут быть временно выключены. Если баннер выключен, то обычные пользователи не должны его получать, при этом админы должны иметь к нему доступ.
- Адаптировать систему для значительного увеличения количества тегов и фичей, при котором допускается увеличение времени исполнения по редко запрашиваемым тегам и фичам.
- Провести нагрузочное тестирование полученного решения и приложить результаты тестирования к решению.
- Иногда получается так, что необходимо вернуться к одной из трех предыдущих версий баннера в связи с найденной ошибкой в логике, тексте и т.д. Измените API таким образом, чтобы можно было просмотреть существующие версии баннера и выбрать подходящую версию.
- Добавить метод удаления баннеров по фиче или тегу, время ответа которого не должно превышать 100 мс, независимо от количества баннеров. В связи с небольшим временем ответа метода, рекомендуется ознакомиться с механизмом выполнения отложенных действий.
- Реализовать интеграционное или E2E-тестирование для остальных сценариев.
- Описать конфигурацию линтера.
- Язык сервиса: предпочтительным будет Go.
- База данных: предпочтительной будет PostgreSQL.
- Для деплоя зависимостей и самого сервиса рекомендуется использовать Docker и Docker Compose.
Возникающие вопросы по заданию, ответов на которые нет в описанных «Условиях», указать в приложенном к проекту README-файл, в котором будет список вопросов и пояснения о том, как получилось решить проблему и почему именно таким способом.
Необходимо предоставить публичный git-репозиторий на любом публичном хосте (GitHub / GitLab / etc), содержащий в master/main ветке:
- Код сервиса.
- Makefile c командами сборки проекта / Описанная в README.md инструкция по запуску.
- Описанные в README.md вопросы/проблемы, с которыми столкнулись, и логика их решений (если требуется).
Вывести список всех возможных команд:
make help
Запустить приложение локально:
make run-local
Запустить приложение с помощью docker-compose:
make run-docker
Сформировать документацию:
make doc
Note
Изменить значения флагов:
- для локального запуска - в первых строках Makefile;
- для запуска с помощью docker-compose - в файле .env.
-
При GET запросе /banner в требованиях к API не указана возвращаемый ответ 400 (Bad Request). Так как в query может быть указано некорректное значение, добавил этот ответ в свой сервис.
-
Описание примеров запросов в Postman содержится в banners-service.json.
-
Интеграционные тесты расположены в файле internal/domains/banner/controllers/http/user_test.go.
-
Использован линтер (golangci-lint) для анализа кода и предотвращения появления разного рода ошибок в ходе его написания. Выбран golangci-lint, так как он включает в себя сразу пакет необходимых линтеров, поддерживает удобную конфигурацию. Из проверки исключены функции w.Write (запись данных в ответ сервера) и logger.Log.Sync (очистка записей журнала логгера перед выходом из приложения).