Проект состоит из четырех независимых сервисов, каждый из которых упакован в собственный Docker-контейнер и выполняет четко определенную роль.
Десктопное веб-приложение, служащее единой точкой входа для пользователя.
- Интерактивная панель управления для мониторинга баланса и заказов.
- Динамическое создание уникального
User IDдля сессии. - Асинхронное обновление данных без перезагрузки страницы.
- Функционал: пополнение счета, создание заказа, просмотр истории заказов с актуальными статусами.
Единая точка входа для всех запросов от фронтенда.
- Маршрутизация запросов к соответствующим бэкенд-сервисам.
- Агрегация Swagger UI, предоставляя единую страницу документации для всего API.
- Настройка CORS для безопасного взаимодействия с фронтендом.
Микросервис, отвечающий за всю финансовую логику.
- Управление счетами: создание, пополнение, проверка баланса.
- Обработка платежей: асинхронно получает события о новых заказах и производит списание средств.
- Обеспечение идемпотентности при обработке платежей для предотвращения двойного списания.
Микросервис для управления жизненным циклом заказов.
- Создание заказов: принимает запросы на создание новых заказов и инициирует процесс оплаты.
- Управление статусами: асинхронно получает результаты оплаты и обновляет статус заказа на
FinishedилиCancelled. - Просмотр истории заказов для конкретного пользователя.
- Бэкенд:
C# 12,.NET 8,ASP.NET Core - Фронтенд:
React,Vite,Material-UI (MUI),axios - База данных:
PostgreSQL(с разделением по схемам для каждого сервиса) - Брокер сообщений:
RabbitMQдля асинхронной коммуникации - API Gateway:
Ocelot - Контейнеризация:
Docker,Docker Compose - Тестирование:
xUnit,Moq
Коммуникация между Orders Service и Payments Service построена на обмене событиями через брокер сообщений RabbitMQ.
- Создание заказа:
Orders Serviceпубликует событиеOrderCreatedEvent. - Обработка платежа:
Payments Serviceподписывается на это событие, обрабатывает платеж и публикует ответное событиеPaymentResultEvent. - Обновление статуса:
Orders Serviceполучает результат и обновляет статус заказа.
Для гарантии того, что ни одно сообщение не будет потеряно, и данные останутся консистентными даже в случае сбоев, применены следующие паттерны:
-
Transactional Outbox (в
Orders ServiceиPayments Service):- При выполнении бизнес-операции (например, создание заказа) сервис атомарно, в рамках одной транзакции базы данных, сохраняет и саму бизнес-сущность (заказ), и исходящее событие в специальную таблицу
OutboxMessages. - Отдельный фоновый процесс периодически опрашивает эту таблицу и отправляет неотправленные сообщения в RabbitMQ. Это гарантирует, что сообщение будет отправлено хотя бы раз (at-least-once).
- При выполнении бизнес-операции (например, создание заказа) сервис атомарно, в рамках одной транзакции базы данных, сохраняет и саму бизнес-сущность (заказ), и исходящее событие в специальную таблицу
-
Transactional Inbox (в
Payments Service):- При получении события
OrderCreatedEventсервис не выполняет бизнес-логику сразу. - Сначала он сохраняет ID входящего сообщения в таблицу
InboxMessagesи только потом, в той же транзакции, обрабатывает платеж. - Перед обработкой любого сообщения он проверяет, нет ли его ID в
Inbox. Это защищает от повторной обработки дублирующихся сообщений.
- При получении события
В критически важной операции — списании денег со счета — обеспечена семантика "exactly-once. Это достигается комбинацией двух паттернов и механизма оптимистичной блокировки:
Transactional Inboxна входе вPayments Serviceгарантирует, что даже если RabbitMQ доставит одно и то же сообщение о создании заказа несколько раз, оно будет обработано только один раз (семантика at-most-once).Transactional Outboxна выходе гарантирует, что событие с результатом платежа будет надежно доставлено (семантика at-least-once).- Оптимистичная блокировка (
Timestamp/RowVersionв EF Core) на сущностиAccountпредотвращает "гонку состояний" при параллельных операциях списания с одного и того же счета.
-
Все просто:
docker-compose up --build -d
-
В браузере:
- Frontend:
http://localhost:3000 - API Gateway Swagger UI:
http://localhost:8000/swagger - RabbitMQ Management:
http://localhost:15672(login:user, pass:password)
- Frontend:
![]() Тестами покрыты ключевая бизнес-логика |
![]() Тестовое покрытие |
- API Gateway Swagger UI:
http://localhost:8000/swagger - API Payments Swagger UI:
http://localhost:8001/swagger - API Orders Swagger UI:
http://localhost:8002/swagger
API Gateway Swagger - Orders API
API Gateway Swagger - Payments API
Успешное создание банковского аккаунта
Успешное пополнение банковского счета на 400 у.е.
Создание заказа стоимостью 500 у.е.; Статус заказа - NEW, ожидаем статус CANCELLED
Спустя время проверяем статус заказа по его id; Ожидаемый результат совпадает с фактическим - статус CANCELLED (нехватка средств)
Создаем новый заказ - стоимость 400 у.е.; Статус заказа - NEW, ожидаем статус FINISHED
Проверяем все заказы по user_id; Все верно - один FINISHED заказ и один CANCELLED
Проверяем, что баланс пользователя изменился после создания заказов
Совершаем покупку на 1200 у.е. (ожидаем статус CANCELLED)
Ожидаемый результат совпадает с фактическим, деньги не списались
Совершаем еще несколько заказов (в сумме на 1000 у.е.); Ожидаем статус FINISHED
Ожидаемый результат совпадает с фактическим, деньги списались, заказы выполнены





