Skip to content

StenlyForse/LoyalCode

Repository files navigation

Верный Код — Тестовое задание (.NET 8)

Микросервисная система на C# / .NET 8, реализующая управление валютными курсами и избранными валютами пользователя.


Задание

Схема БД (PostgreSQL)

Таблица currency

Поле Тип Описание
id uuid Первичный ключ
name text Название валюты
rate numeric Курс к рублю

Таблица user

Поле Тип Описание
id uuid Первичный ключ
name text Имя пользователя
password text BCrypt-хеш пароля

Пользователь может добавлять валюты в избранное (favorites) — связь many-to-many между user и currency.

Требования к реализации

  1. Микросервис миграции БД
  2. Фоновый сервис — получение курсов ЦБР и заполнение таблицы currency
  3. UserService — регистрация, логин, логаут (Clean Architecture + CQRS)
  4. FinanceService — получение курсов валют по пользователю (Clean Architecture + CQRS)
  5. JWT-авторизация
  6. API Gateway для обоих микросервисов
  7. Unit-тесты для UserService и FinanceService

Архитектура

┌─────────────────────────────────────────────────────┐
│                    API Gateway                       │
│              (YARP, :5000)                          │
│   JWT validation + blacklist check + routing        │
└──────────────┬──────────────────────┬───────────────┘
               │                      │
               ▼                      ▼
   ┌───────────────────┐   ┌────────────────────┐
   │   UserService     │   │  FinanceService     │
   │   (:5001)         │   │  (:5002)            │
   │ register/login/   │   │ GET rates by user   │
   │ logout            │   │ add/remove favorite │
   └───────────────────┘   └────────────────────┘
               │                      │
               └──────────┬───────────┘
                          ▼
                ┌──────────────────┐
                │   PostgreSQL     │
                │  loyalcode DB    │
                └──────────────────┘
                          ▲
              ┌───────────┴────────────┐
              │   MigrationService     │  (запускается первым, создаёт схему)
              │   CurrencyUpdater      │  (фоновый, обновляет курсы ЦБР)
              └────────────────────────┘

Слоистость (Clean Architecture, UserService и FinanceService)

Domain → Application → Infrastructure → API
  • Domain — сущности, доменные исключения, без зависимостей
  • Application — команды/запросы (MediatR), интерфейсы репозиториев, валидаторы (FluentValidation), pipeline behaviors
  • Infrastructure — EF Core, реализации репозиториев, сервисы (JWT, BCrypt)
  • API — контроллеры, регистрация зависимостей

Структура проекта

src/
├── Gateway/
│   └── LoyalCode.Gateway/              # YARP API Gateway (:5000)
└── Services/
    ├── Migration/
    │   └── LoyalCode.Migration/        # Worker: EF Core миграции при старте
    ├── CurrencyUpdater/
    │   └── LoyalCode.CurrencyUpdater/  # Worker: опрос ЦБР каждые N минут
    ├── UserService/
    │   ├── LoyalCode.UserService.Domain/
    │   ├── LoyalCode.UserService.Application/
    │   ├── LoyalCode.UserService.Infrastructure/
    │   └── LoyalCode.UserService.API/  # (:5001)
    └── FinanceService/
        ├── LoyalCode.FinanceService.Domain/
        ├── LoyalCode.FinanceService.Application/
        ├── LoyalCode.FinanceService.Infrastructure/
        └── LoyalCode.FinanceService.API/ # (:5002)
tests/
├── LoyalCode.UserService.Tests/
└── LoyalCode.FinanceService.Tests/

API

Gateway (:5000) — точка входа

Все запросы идут через Gateway. Он валидирует JWT и проксирует запросы к сервисам.

UserService — /api/auth

Метод Путь Auth Описание
POST /api/auth/register Регистрация ({ name, password })
POST /api/auth/login Логин, возвращает JWT-токен
POST /api/auth/logout JWT Логаут, инвалидирует токен

FinanceService — /api/currency

Метод Путь Auth Описание
GET /api/currency/rates JWT Курсы избранных валют пользователя
POST /api/currency/favorites JWT Добавить валюту в избранное (body: guid)
DELETE /api/currency/favorites/{currencyId} JWT Удалить валюту из избранного

Технологии

Технология Версия Назначение
.NET 8.0.125 Runtime/SDK
ASP.NET Core 8.0 Web API
Entity Framework Core 8.0.0 ORM
Npgsql EF Provider 8.0.0 PostgreSQL
MediatR 12.4.1 CQRS pipeline
FluentValidation 12.1.1 Валидация команд
YARP 2.2.0 Reverse Proxy / API Gateway
BCrypt.Net-Next 4.0.3 Хеширование паролей
xUnit + Moq + FluentAssertions Unit-тесты

Запуск

Требования

  • .NET 8 SDK
  • PostgreSQL (локально или в Docker)

Настройка БД

Создайте базу данных и убедитесь, что строка подключения в appsettings.json каждого сервиса корректна:

"ConnectionStrings": {
  "DefaultConnection": "Host=localhost;Port=5432;Database=loyalcode;Username=postgres;Password=postgres"
}

В production используйте переменные окружения или dotnet user-secrets вместо хранения credentials в конфиге.

Порядок запуска

# 1. Миграции (создаёт схему БД и завершается)
dotnet run --project src/Services/Migration/LoyalCode.Migration

# 2. Обновление курсов ЦБР (фоновый, запускать параллельно или отдельно)
dotnet run --project src/Services/CurrencyUpdater/LoyalCode.CurrencyUpdater

# 3. UserService
dotnet run --project src/Services/UserService/LoyalCode.UserService.API

# 4. FinanceService
dotnet run --project src/Services/FinanceService/LoyalCode.FinanceService.API

# 5. Gateway (единая точка входа)
dotnet run --project src/Gateway/LoyalCode.Gateway

После запуска:

  • Swagger UserService: http://localhost:5001/swagger
  • Swagger FinanceService: http://localhost:5002/swagger
  • Gateway: http://localhost:5000
  • Health checks: http://localhost:500X/health

Запуск тестов

dotnet test

Ключевые решения

JWT + blacklist. Токены подписываются HS256. При логауте jti токена добавляется в in-memory blacklist (ConcurrentDictionary). Gateway при каждом запросе проверяет blacklist через GET /internal/tokens/{jti}/is-revoked у UserService.

Upsert курсов. CurrencyUpdater загружает XML с ЦБР, нормализует курс (value / nominal), затем обновляет существующие записи или добавляет новые.

FluentValidation в MediatR pipeline. ValidationBehavior<TRequest,TResponse> перехватывает все команды до хендлера. Невалидные запросы возвращают 400 Bad Request с перечнем ошибок.

Избранные валюты. Отдельная таблица user_favorites (many-to-many). GetRatesByUser делает один JOIN вместо N+1 запросов.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages