/masstransit-demo

Demo of using MassTransit for distibuted applications creation.

Primary LanguageC#MIT LicenseMIT

MassTransit Advanced Example

Техническое описание

Данный проект представляет собой демо-версию микросервисной архитектуры с оркестрацией и ипользованием service bus. В качестве реализации service bus был взят фреймворк MassTransit.

Суть проекта

Приложение представляет собой сервер для обработки клиентских заказов. Обработка заказаов состоит из следующих действий:

  • Подтверждение заказа
  • Резервация денег на карте клиента
  • Подтверждение/отклонение заказа менеджером
  • Доставка заказа
  • Получение обратной связи от клиента
  • Архивация заказа(записать всю сущность в базу данных)

Допущения

  • После подтверждения заказа пользователь не может сделать новый заказ или изменить текущую корзину, пока предыдущий не пройдет все стадии(его сага должна завершиться)

Схема микросервисов

![Microservice Diagram](/ReadmeFiles/Microservice Diagram.png)

Миросервисы

API service

Бизнес логика

  • API для пользователя и менеджера
  • Консьюмеры, которые обрабатывают события о заказе (в данном проекте эти события просто выводятся на консоль, но в реальном проекте скорее всего будет отправляться уведомления по веб-сокетам)

Consumers

  • NewOrderConfirmationRequestedConsumer: обрабатывает сообщения о том, что кто-то из менеджеров должен подтвердить заказ
  • OrderRejectedConsumer: обрабатывает сообщения о том, что заказ был отклонен менеджером
  • FeedbackRequestedConsumer: обрабатывает сообщения о том, что был запрос на обратную связь от клиента
  • GetArchievedOrderResponseConsumer: обрабатывает сообщения о том, что запрос на получение архированного заказа был обработан и пришло сообщение с данными о заказе

Cart service

Бизнес логика

  • Добавления/удаления товаров из корзины (данные хранятся в бд)
  • Обработка запроса на получение актуальной корзины

Особенности

  • Если в базе уже содержится добавляемый товар, то цена товара берется из базы. Если подобного товара в базе нет, то цена товара рандомится(чобы не услонять API)

Consumers

  • AddCartPositionConsumer: добавляет товар к заказу
  • RemoveCartPositionConsumer: убирает товар из заказа
  • GetCartConsumer: возвращает запрашиваемую корзину

Delivery service

Бизнес логика

  • Имитация пролонгированной во времени операции - доставки заказа

Consumers

  • DeliveryOrderConsumer: имитирует доставку заказа

Feedback service

Бизнес логика

  • Добавление отзывов пользователей (данные хранятся в бд)
  • Отправка сохранённых отзывов по запросу

Consumers

  • AddFeedbackConsumer: добавляет отзыв пользователя
  • GetOrderFeedbackConsumer: возвращает запрашиваемый отзыв по конкретному заказу

Payment service

Бизнес логика

  • Резервирует указанную сумму
  • Отменяет резервацию указанной суммы

Особенности

  • Данный сервис максимально "тупой", он прост логирует отправляемые ему команды и все

Consumers

  • ReserveMoneyConsumer: резервирует необходимую сумму
  • UnreserveMoneyConsumer: отменяет резервацию необходимой суммы

History service

Бизнес логика

  • Сохранение финализированных саг (данные хранятся в бд)
  • Отправка сохранённых отзывов по запросу

Consumers

  • ArchivedOrderConsumer: сохраняет сагу
  • GetOrderFromArchiveConsumer: возвращает информацию о завершённой саге

Оркестратор

Техническое описание

В данном сервисе в основном содержатся саги (паттерн диспетчер процессов), который работают на машинах состояний (используется библиотека Automatonymous).

Саги

OrderStateMachine

Данная сага описывает жизненный цикл заказа. Данная сага хранится персистентно с использованием EntityFrameworkCore.

Реагирует на события:

  • OrderSubmitted
  • OrderConfirmed
  • OrderRejected
  • OrderDelivered
  • ReceivedFeedback
  • OrderAborted

Workflow schema

Saga workflow

[SagaWorkflow](ReadmeFiles/Saga Workflow Diagram.png)

ArchievedOrderStateMachine

Агрегирующая сага. Данная сага обращается к трем микросервисам(CartService, HistoryService, FeedbackService) для получения заархивированного заказа. Вполне стандартный кейс для использования стейт машины. Данные собираются паралелльно с помощью композит ивентов.

Подводные камни данной саги:

  • Не умеет работать в паре с IRequestClient, из-за чего необходимо сохранять RequestId и ResponseAddress в инстансе

Консьюмеры

  • GetOrderStateConsumer: получить состояние заказа по Id
  • GetAllOrdersStateConsumer: получить состояние всех заказов
  • GetArchivedOrderConsumer: получить заархивированный заказ (альтернатива ArchievedOrderStateMachine).

Инструкция по деплою

  1. Запусть build.sh в корневой директории проекта. (Данный скрипт собирает докер образы всех сервисов и создаёт файлы конфигов)
  2. 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
  • Инциализаторы в стейт машинах не могут прокидывать хедеры через двойное подчеркивание