# README
gRPC Basic Auth
Необходимо написать веб сервис который занимается хранением профилей пользователей и их авторизацией.
Профиль имеет набор полей:
- id (uuid, unique)
- username (unique)
- password
- admin (bool)
Сервис должен иметь набор ручек (gRPC): Создание пользователя, Выдача списка пользователей Выдача юзера по id Изменение и удаление профиля
Сервис использует basic access authentication (https://en.wikipedia.org/wiki/Basic_access_authentication)
Просматривать профили могут все зарегистрированные пользователи. Админ может создавать, изменять и удалять профили.
Для хранения данных профилей необходимо реализовать примитивную in memory базу данных, не используя сторонние решения.
Зависимости для разработки
- golangci-lint -- Для поддержки кода в хорошем состоянии
- protoc + proto-gen-go & protoc-gen-go-grpc -- Для компиляции proto файлов
- pre-commit -- Для исключения возможности создавать коммиты с "грязным" кодом
- docker(with compose) -- Для разворачивания окружения
- make -- Для упрощения работы с окружением
Запуск
Перед запуском проекта нужно прописать необходимые переменные окружения.
Docker compose использует .env
файл, пример которого находится в .env.example
.
После создания .env
файла, можно запустить проект с помощью команды make run
,
что запустит проект через docker compose.
Тестирование
В проекте реализованы unit тесты для Repository
и
сквозные end-to-end тесты для всего приложения.
Unit тесты
Запуск unit тестов осуществляется командой make test-unit
.
End-to-end тесты
Для end-to-end тестов приложение запускается в изолированном docker контейнере, с использованием testcontainers. Сами же тесты вызываются из хостовой системы.
End-to-end тесты не читают .env файл, поэтому необходимо прописать переменные окружения вручную.
Запуск end-to-end тестов осуществляется командой make test-e2e
.
Архитектура приложения
Директория proto
содержит описание gRPC сервиса и сгенерированный код.
Директория internal
содержит основной код приложения.
В директории tests
содержатся end-to-end тесты.
В приложении используется чистая архитектура, с разделением на слои:
- transport -- слой представления: обслуживание gRPC запросов
- use_cases -- слой бизнес-логики
- infrastructure -- источник данных: обращения к базе данных
Альтернативная структура
Причиной выбрать подобную структуру директорий послужил факт того, что данное приложение является примитивным CRUD интерфейсом для лишь одной сущности. В приложении с несколькими агрегатами и сложной бизнес-логикой архитектура могла бы выглядеть так:
internal/
├── main.go
├── common/
│ ├── context_logger.go
│ ├── disabled_logger.go
│ └── flagged_error.go
└── user/
├── models.go
├── usecases.go
├── handlers.go
├── repository.go
└── repository_test.go
Решения и примечания
-
Для обработки доменных ошибок в пакете internal/common был реализован простой механизм обработки ошибок, описанный Nate Finch здесь Error Flags
-
Так как проект является примитивным CRUD интерфейсом и не содержит сложной бизнес-логики, было принято решение отказаться от слоя бизнес-логики и использовать обычную анемичную модель для представления данных в приложении.
-
API приложения следует CQS принципу, поэтому команды апи, такие, как создание и изменение пользователя, возвращают пустой объект.
-
Для хранения данных был выбран
sync.Map
, так как он является потокобезопасным -
Так как
sync.Map
не предоставляет возможности получить количество записей,Repository.GetAll
создаёт пустой срез и на каждой итерации расширяет его при необходимости (append). Это негативно сказывается на производительности. Можно было бы считать количество записей в отдельной переменной, но на данном этапе в этом нет необходимости. -
Так как для создания пользователя необходимо авторизоваться, как администратор, было принято решение создавать первого администратора при старте приложения, используя значения из переменных окружения.
-
Для хранения паролей пользователей используется bcrypt.