Реализация сервиса корзины CartService
Выбор применяемых технологий
- WEB API ASP.NET CORE
- ORM Entity Framework (подключен Dapper для ReadOnly операций p.s. не до конца реализован)
- Local db (MSSQL)
- xUnit (unit-тестирование, интеграционное)
- IronPdf (конвертация в pdf)
- AutoMapper
- Hangfire (background jobs)
- Swagger
Архитектура
Было принято решение следовать классической clean архитектуре, придерживаться REST-стиля API.
Также стоит отметить, что некоторые модули могут иметь High coupling, но я старался придерживаться solid
p.s. не использовал Docker
Проект состоит из 4 проектов.
- CartTest.Data
- CartTest.IntegrationTesting
- CartTest.Service
- CartTest.WebAPI
В проекте CartTest.Data созданы сущности + репозитории для Dapper и EntityFramework
Сущности - Product, CartItem, Cart, Report, WebHook (по-хорошему, сделать связи через fluent api)
Реализован отдельный Readonly репозиторий (asNoTracking).
Dapper реализует IRepository (принимает generic методы). Подключение к базе через инъекцию IDatabaseConnectionFactory
На будущее - есть мысли реализовать UOW/Repository как стандартный вариант или CQS команды для подключения Dapper.
CartTest.Service создан для описания кейсов, взаимодействия с нижнем уровнем Data, описания DTO-объектов и основной бизнес-логики
Создан профиль для маппинга моделей в DTO.
На будущее - определен класс OperationResult для обработки операций и избегания try/catch. Необходимо обернуть большинство методов и обработку разных кейсов
CartTest.WebAPI api методы, также зарегистрированы сервисы/DI.
Hangfire запускает раз в сутки отчет GenerationPdf()
Был использован AnonymousId middleware для идентификации анонимного неавторизованного пользователя посредством cookie
Подводный камень - если зарегистрированный пользователь набрал себе корзину как аноним и потом входит в аккаунт. Надо будет реализовать отдельное решение
По поводу веб-хуков и выполнения background-задачи
Для пользователя определен API для регистрации веб-хука (логику подписки можно будет менять)
Определен класс для планировщика DeleteCartOnExpirationJob, который выполняется по cron'у раз в сутки.
Он проверяет одним запросом просроченные корзины, если такие есть, планировщик удаляет их из базы
Далее проверяем есть ли подписанные вебхуки, берем callbackUrl,затем запускаем метод ExecuteJobAsync клиента IWebHookClient по каждому хуку и передаем callbackUrl
ExecuteJobAsync вызывает post запрос HttpClient'а, который честно стучится по retry 3 раза, добавляем обработку в TryHandler (+ отмена операции CancellationToken)