Newer
Older
ReliableMessaging / src / ApplicationCore / Services / OrderService.cs
@Derek Comartin Derek Comartin on 10 Apr 2023 3 KB Init
using System;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Amazon.S3;
using Amazon.S3.Model;
using Ardalis.GuardClauses;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
using Newtonsoft.Json;
using NServiceBus;
using Polly;

namespace Microsoft.eShopWeb.ApplicationCore.Services;

public class OrderService : IOrderService
{
    private readonly IRepository<Order> _db;
    private readonly IUriComposer _uriComposer;
    private readonly IMessageSession _broker;
    private readonly IRepository<Basket> _basketRepository;
    private readonly IRepository<CatalogItem> _itemRepository;

    public OrderService(IRepository<Basket> basketRepository,
        IRepository<CatalogItem> itemRepository,
        IRepository<Order> orderRepository,
        IUriComposer uriComposer,
        IMessageSession messageSession)
    {
        _db = orderRepository;
        _uriComposer = uriComposer;
        _broker = messageSession;
        _basketRepository = basketRepository;
        _itemRepository = itemRepository;
    }

    public async Task CreateOrderAsync(int basketId, Address shippingAddress)
    {
        var basketSpec = new BasketWithItemsSpecification(basketId);
        var basket = await _basketRepository.GetBySpecAsync(basketSpec);

        Guard.Against.NullBasket(basketId, basket);
        Guard.Against.EmptyBasketOnCheckout(basket.Items);

        var catalogItemsSpecification = new CatalogItemsSpecification(basket.Items.Select(item => item.CatalogItemId).ToArray());
        var catalogItems = await _itemRepository.ListAsync(catalogItemsSpecification);

        var items = basket.Items.Select(basketItem =>
        {
            var catalogItem = catalogItems.First(c => c.Id == basketItem.CatalogItemId);
            var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, _uriComposer.ComposePicUri(catalogItem.PictureUri));
            var orderItem = new OrderItem(itemOrdered, basketItem.UnitPrice, basketItem.Quantity);
            return orderItem;
        }).ToList();

        var order = new Order(basket.BuyerId, shippingAddress, items);
        await _db.AddAsync(order);
        await _db.SaveChangesAsync();
        
        var orderPlacedEvent = new OrderPlacedEvent(order.Id);
        var retry = Policy.Handle<Exception>()
            .WaitAndRetryAsync(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3) });

        var fallback = Policy.Handle<Exception>().FallbackAsync(async _ =>
        {
            var s3Client = new AmazonS3Client();
            await s3Client.PutObjectAsync(new PutObjectRequest
            {
                Key = Guid.NewGuid().ToString(),
                ContentBody = JsonConvert.SerializeObject(orderPlacedEvent)
            });
        });

        await fallback.WrapAsync(retry).ExecuteAsync(async () =>
        {
            await _broker.Publish(orderPlacedEvent);
        });
    }
}

public record OrderPlacedEvent(int OrderId) : IEvent;