Проблема:
В компании есть много различных микросервисов. Многие из них так или иначе хотят взаимодействовать с балансом пользователя. На архитектурном комитете приняли решение централизовать работу с балансом пользователя в отдельный сервис.
Задача:
Необходимо реализовать микросервис для работы с балансом пользователей (зачисление средств, списание средств, перевод средств от пользователя к пользователю, а также метод получения баланса пользователя). Сервис должен предоставлять HTTP API и принимать/отдавать запросы/ответы в формате JSON. Подробнее тестовое задание описано тут.
Решение:
Для обеспечения корректности данных операции над баланасом пользователя выполняются в транзакциях со строгой изоляцией Serializable
. С таким уровнем изоляции каждая транзакция будет исполнятся будто последовательно, что обеспечивает корректность значений при параллельных запросах на обновление баланса.
Архитектура проекта была вдохнавлена go-clean-template. В слое entity
находится модель данных, в слое usecase
описаны пути работы с моделью данных, в repo
работа с хранилищем данных.
АPI микросервиса предоставляет следующий функционал:
- Создание нового аккаунта
- Получение полей аккаунта
- Обновление баланса аккаунта
- Перевод средств между аккаунтами
- Получение истории транзакций аккаунта с возможностью сортировки и пагинации
После успешного запуска проекта интерактивная документация API доступна по ссылке.
Развёртывание:
Запуск сервера и СУБД:
make run
Запуск юнит-тестов:
make unit-test
Запуск интеграционных тестов:
make integration-test
Генерация swagger документации:
make run-swag
Остановка сервера и СУБД, удаление контейнеров и сети:
make down
Конфигурирование:
Через переменные окружения можно настроить реквизиты доступа к СУБД. Чтобы их задать, необходимо создать и заполнить .env
файл. Пример его заполнения в файле .env.example
. Для быстрой демонстрации проекта в docker-compose.yml
прописаны значения переменных окружения по умолчанию.
Для конфигурирования не конфиденциальных переменных достаточно прописать их в config/config.yml
. При изменении портов необходимо изменить обновить информацию в коде интеграционных тестов. Код инициализации СУБД находится в файле init/init.sql
.
Примеры:
Создание аккаунта
curl -X POST "http://0.0.0.0:8080/v1/account/"
"data": {
"id": 1,
"balance": 0,
"created_dt": "2022-07-11T18:37:27.126846Z"
}
Получить аккаунт по ID
curl -X GET "http://0.0.0.0:8080/v1/account/1"
"data": {
"id": 1,
"balance": 56,
"created_dt": "2022-07-11T18:37:27.126846Z"
}
Обновить баланс, начислить 56 рублей
curl -X PUT "http://0.0.0.0:8080/v1/account/1?amount=56"
"data": {
"id": 1,
"balance": 56,
"created_dt": "2022-07-11T18:37:27.126846Z"
}
Обновить баланс, снять 16 рублей
curl -X PUT "http://0.0.0.0:8080/v1/account/1?amount=-16"
"data": {
"id": 1,
"balance": 40,
"created_dt": "2022-07-11T18:37:27.126846Z"
}
Перевести 10 рублей от аккаунта 1 аккаунту 2 (должен был быть создан заранее)
curl -X PUT "http://0.0.0.0:8080/v1/account/amount/1/transfer/2?amount=10"
"data": {
"accrualAccount": {
"id": 2,
"balance": 10,
"created_dt": "2022-07-11T18:46:41.585732Z"
},
"redeemAccount": {
"id": 1,
"balance": 30,
"created_dt": "2022-07-11T18:37:27.126846Z"
}
}
Получить историю транзакций аккаунта 1 c сортировкой по убыванию даты операции (по умолчанию)
curl -X GET "http://0.0.0.0:8080/v1/account/history/1?limit=15&offset=0"
"data": [
{
"id": 1,
"trans_dt": "2022-07-11T18:50:21.906308Z",
"account_id": 1,
"doc_num": -999,
"type": "accrual",
"amount": 56
},
{
"id": 2,
"trans_dt": "2022-07-11T18:50:25.121921Z",
"account_id": 1,
"doc_num": -999,
"type": "redeem",
"amount": -16
},
{
"id": 3,
"trans_dt": "2022-07-11T18:50:27.348807Z",
"account_id": 1,
"doc_num": 2,
"type": "redeem",
"amount": -10
}
]
Получить историю транзакций аккаунта 1 c сортировкой по возрастанию суммы операции
curl -X GET "http://0.0.0.0:8080/v1/account/history/1?limit=15&offset=0&sort=amount&isDecreasing=false"
"data": [
{
"id": 1,
"trans_dt": "2022-07-11T18:50:21.906308Z",
"account_id": 1,
"doc_num": -999,
"type": "accrual",
"amount": 56
},
{
"id": 3,
"trans_dt": "2022-07-11T18:50:27.348807Z",
"account_id": 1,
"doc_num": 2,
"type": "redeem",
"amount": -10
},
{
"id": 2,
"trans_dt": "2022-07-11T18:50:25.121921Z",
"account_id": 1,
"doc_num": -999,
"type": "redeem",
"amount": -16
}
]