Данный проект представляет собой демо-версию микросервисной архитектуры с оркестрацией и ипользованием service bus. В качестве реализации service bus был взят фреймворк MassTransit.
Приложение представляет собой сервер для обработки клиентских заказов. Обработка заказаов состоит из следующих действий:
- Подтверждение заказа
- Резервация денег на карте клиента
- Подтверждение/отклонение заказа менеджером
- Доставка заказа
- Получение обратной связи от клиента
- Архивация заказа(записать всю сущность в базу данных)
- После подтверждения заказа пользователь не может сделать новый заказ или изменить текущую корзину, пока предыдущий не пройдет все стадии(его сага должна завершиться)
![Microservice Diagram](/ReadmeFiles/Microservice Diagram.png)
- API для пользователя и менеджера
- Консьюмеры, которые обрабатывают события о заказе (в данном проекте эти события просто выводятся на консоль, но в реальном проекте скорее всего будет отправляться уведомления по веб-сокетам)
- NewOrderConfirmationRequestedConsumer: обрабатывает сообщения о том, что кто-то из менеджеров должен подтвердить заказ
- OrderRejectedConsumer: обрабатывает сообщения о том, что заказ был отклонен менеджером
- FeedbackRequestedConsumer: обрабатывает сообщения о том, что был запрос на обратную связь от клиента
- GetArchievedOrderResponseConsumer: обрабатывает сообщения о том, что запрос на получение архированного заказа был обработан и пришло сообщение с данными о заказе
- Добавления/удаления товаров из корзины (данные хранятся в бд)
- Обработка запроса на получение актуальной корзины
- Если в базе уже содержится добавляемый товар, то цена товара берется из базы. Если подобного товара в базе нет, то цена товара рандомится(чобы не услонять API)
- AddCartPositionConsumer: добавляет товар к заказу
- RemoveCartPositionConsumer: убирает товар из заказа
- GetCartConsumer: возвращает запрашиваемую корзину
- Имитация пролонгированной во времени операции - доставки заказа
- DeliveryOrderConsumer: имитирует доставку заказа
- Добавление отзывов пользователей (данные хранятся в бд)
- Отправка сохранённых отзывов по запросу
- AddFeedbackConsumer: добавляет отзыв пользователя
- GetOrderFeedbackConsumer: возвращает запрашиваемый отзыв по конкретному заказу
- Резервирует указанную сумму
- Отменяет резервацию указанной суммы
- Данный сервис максимально "тупой", он прост логирует отправляемые ему команды и все
- ReserveMoneyConsumer: резервирует необходимую сумму
- UnreserveMoneyConsumer: отменяет резервацию необходимой суммы
- Сохранение финализированных саг (данные хранятся в бд)
- Отправка сохранённых отзывов по запросу
- ArchivedOrderConsumer: сохраняет сагу
- GetOrderFromArchiveConsumer: возвращает информацию о завершённой саге
В данном сервисе в основном содержатся саги (паттерн диспетчер процессов), который работают на машинах состояний (используется библиотека Automatonymous).
OrderStateMachine
Данная сага описывает жизненный цикл заказа. Данная сага хранится персистентно с использованием EntityFrameworkCore.
- OrderSubmitted
- OrderConfirmed
- OrderRejected
- OrderDelivered
- ReceivedFeedback
- OrderAborted
[SagaWorkflow](ReadmeFiles/Saga Workflow Diagram.png)
ArchievedOrderStateMachine
Агрегирующая сага. Данная сага обращается к трем микросервисам(CartService, HistoryService, FeedbackService) для получения заархивированного заказа. Вполне стандартный кейс для использования стейт машины. Данные собираются паралелльно с помощью композит ивентов.
Подводные камни данной саги:
- Не умеет работать в паре с IRequestClient, из-за чего необходимо сохранять RequestId и ResponseAddress в инстансе
- GetOrderStateConsumer: получить состояние заказа по Id
- GetAllOrdersStateConsumer: получить состояние всех заказов
- GetArchivedOrderConsumer: получить заархивированный заказ (альтернатива ArchievedOrderStateMachine).
- Запусть
build.sh
в корневой директории проекта. (Данный скрипт собирает докер образы всех сервисов и создаёт файлы конфигов) docker-compose up
- Swagger UI:
127.0.0.1:80/swagger
- RabbitMQ:
127.0.0.1:15672
(guest/guest) - Prometeus:
127.0.0.1:9090
- Grafana:
127.0.0.1:3000
(admin/admin)
- NRT плохо работает внутри стейт машины из-за ее декларативности, читаемость падает. Мы отключили фичу в файле со стейт машиной и инстансом
- В персистентных (а лучше во всех) сагах нужно использовать outbox, чтобы сага сначала транзитилась в нужный стейт, а потом уже отправляла сообщения
- При проверке одноного инстанса в юнит тестах на разные стейты с помощью метода Exists оба исхода могут оказатся положительными, так как метод awaitable
- Инциализаторы в стейт машинах не могут прокидывать хедеры через двойное подчеркивание