diff --git a/src/HelloShop.OrderingService/DistributedEvents/EventHandling/OrderPaymentSucceededDistributedEventHandler.cs b/src/HelloShop.OrderingService/DistributedEvents/EventHandling/OrderPaymentSucceededDistributedEventHandler.cs new file mode 100644 index 0000000..34c6cc0 --- /dev/null +++ b/src/HelloShop.OrderingService/DistributedEvents/EventHandling/OrderPaymentSucceededDistributedEventHandler.cs @@ -0,0 +1,42 @@ +// Copyright (c) HelloShop Corporation. All rights reserved. +// See the license file in the project root for more information. + +using HelloShop.OrderingService.DistributedEvents.Events; +using HelloShop.OrderingService.Entities.Orders; +using HelloShop.OrderingService.Infrastructure; +using HelloShop.ServiceDefaults.DistributedEvents.Abstractions; +using Microsoft.EntityFrameworkCore; + +namespace HelloShop.OrderingService.DistributedEvents.EventHandling +{ + public class OrderPaymentSucceededDistributedEventHandler(OrderingServiceDbContext dbContext, IDistributedEventBus distributedEventBus) : IDistributedEventHandler + { + public async Task HandleAsync(OrderPaymentSucceededDistributedEvent @event) + { + var strategy = dbContext.Database.CreateExecutionStrategy(); + + await strategy.ExecuteAsync(async () => + { + await using var transaction = await dbContext.Database.BeginTransactionAsync(); + + DbSet orders = dbContext.Set(); + + Order order = await orders.FindAsync(@event.OrderId) ?? throw new Exception($"Order with id {@event.OrderId} not found"); + + await orders.Entry(order).Collection(i => i.OrderItems).LoadAsync(); + + order.OrderStatus = OrderStatus.Paid; + + await dbContext.SaveChangesAsync(); + + await transaction.CommitAsync(); + + var orderStockList = order.OrderItems.Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.Units)); + + var integrationEvent = new OrderPaidDistributedEvent(order.Id, orderStockList); + + await distributedEventBus.PublishAsync(integrationEvent); + }); + } + } +} diff --git a/src/HelloShop.OrderingService/DistributedEvents/Events/OrderPaidDistributedEvent.cs b/src/HelloShop.OrderingService/DistributedEvents/Events/OrderPaidDistributedEvent.cs new file mode 100644 index 0000000..1f928a7 --- /dev/null +++ b/src/HelloShop.OrderingService/DistributedEvents/Events/OrderPaidDistributedEvent.cs @@ -0,0 +1,9 @@ +// Copyright (c) HelloShop Corporation. All rights reserved. +// See the license file in the project root for more information. + +using HelloShop.ServiceDefaults.DistributedEvents.Abstractions; + +namespace HelloShop.OrderingService.DistributedEvents.Events +{ + public record OrderPaidDistributedEvent(int OrderId, IEnumerable OrderStockItems) : DistributedEvent; +} diff --git a/src/HelloShop.OrderingService/DistributedEvents/Events/OrderPaymentFailedDistributedEvent.cs b/src/HelloShop.OrderingService/DistributedEvents/Events/OrderPaymentFailedDistributedEvent.cs new file mode 100644 index 0000000..248f5b3 --- /dev/null +++ b/src/HelloShop.OrderingService/DistributedEvents/Events/OrderPaymentFailedDistributedEvent.cs @@ -0,0 +1,14 @@ +// Copyright (c) HelloShop Corporation. All rights reserved. +// See the license file in the project root for more information. + +using HelloShop.ServiceDefaults.DistributedEvents.Abstractions; + +namespace HelloShop.OrderingService.DistributedEvents.Events +{ + public record OrderPaymentFailedDistributedEvent : DistributedEvent + { + public int OrderId { get; } + + public OrderPaymentFailedDistributedEvent(int orderId) => OrderId = orderId; + } +} diff --git a/src/HelloShop.OrderingService/DistributedEvents/Events/OrderPaymentSucceededDistributedEvent.cs b/src/HelloShop.OrderingService/DistributedEvents/Events/OrderPaymentSucceededDistributedEvent.cs new file mode 100644 index 0000000..07e0cb4 --- /dev/null +++ b/src/HelloShop.OrderingService/DistributedEvents/Events/OrderPaymentSucceededDistributedEvent.cs @@ -0,0 +1,9 @@ +// Copyright (c) HelloShop Corporation. All rights reserved. +// See the license file in the project root for more information. + +using HelloShop.ServiceDefaults.DistributedEvents.Abstractions; + +namespace HelloShop.OrderingService.DistributedEvents.Events +{ + public record OrderPaymentSucceededDistributedEvent(int OrderId) : DistributedEvent; +} diff --git a/src/HelloShop.OrderingService/Extensions/Extensions.cs b/src/HelloShop.OrderingService/Extensions/Extensions.cs index d4cfde7..2acae5e 100644 --- a/src/HelloShop.OrderingService/Extensions/Extensions.cs +++ b/src/HelloShop.OrderingService/Extensions/Extensions.cs @@ -47,6 +47,7 @@ namespace HelloShop.OrderingService.Extensions }); builder.Services.AddHostedService(); + builder.Services.AddHostedService(); } public static WebApplication MapApplicationEndpoints(this WebApplication app) diff --git a/src/HelloShop.OrderingService/Workers/PaymentWorker.cs b/src/HelloShop.OrderingService/Workers/PaymentWorker.cs new file mode 100644 index 0000000..05cda9f --- /dev/null +++ b/src/HelloShop.OrderingService/Workers/PaymentWorker.cs @@ -0,0 +1,50 @@ +// Copyright (c) HelloShop Corporation. All rights reserved. +// See the license file in the project root for more information. + +using HelloShop.OrderingService.DistributedEvents.Events; +using HelloShop.OrderingService.Entities.Orders; +using HelloShop.OrderingService.Infrastructure; +using HelloShop.ServiceDefaults.DistributedEvents.Abstractions; +using Microsoft.EntityFrameworkCore; + +namespace HelloShop.OrderingService.Workers +{ + public class PaymentWorker(IServiceScopeFactory serviceScopeFactory, ILogger logger) : BackgroundService + { + protected async override Task ExecuteAsync(CancellationToken stoppingToken) + { + + while (!stoppingToken.IsCancellationRequested) + { + if (logger.IsEnabled(LogLevel.Information)) + { + logger.LogInformation("{Worker} background task is doing background work.", GetType().Name); + } + + using var scope = serviceScopeFactory.CreateAsyncScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + var distributedEventBus = scope.ServiceProvider.GetRequiredService(); + + bool paymentSucceeded = Random.Shared.NextDouble() > 0.3; + + try + { + var orders = await dbContext.Set().Include(o => o.OrderItems).Where(o => o.OrderStatus == OrderStatus.StockConfirmed).ToListAsync(stoppingToken); + + foreach (var order in orders) + { + DistributedEvent @event = paymentSucceeded ? new OrderPaymentSucceededDistributedEvent(order.Id) : new OrderPaymentFailedDistributedEvent(order.Id); + + await distributedEventBus.PublishAsync(@event, stoppingToken); + } + } + catch (Exception ex) + { + logger.LogError(ex, "Error occurred executing {Worker} background task.", GetType().Name); + } + + await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken); + } + } + } +} diff --git a/src/HelloShop.ProductService/DistributedEvents/EventHandling/OrderPaidDistributedEventHandler.cs b/src/HelloShop.ProductService/DistributedEvents/EventHandling/OrderPaidDistributedEventHandler.cs new file mode 100644 index 0000000..a21c94d --- /dev/null +++ b/src/HelloShop.ProductService/DistributedEvents/EventHandling/OrderPaidDistributedEventHandler.cs @@ -0,0 +1,25 @@ +// Copyright (c) HelloShop Corporation. All rights reserved. +// See the license file in the project root for more information. + +using HelloShop.ProductService.DistributedEvents.Events; +using HelloShop.ProductService.Entities.Products; +using HelloShop.ProductService.Infrastructure; +using HelloShop.ServiceDefaults.DistributedEvents.Abstractions; + +namespace HelloShop.ProductService.DistributedEvents.EventHandling +{ + public class OrderPaidDistributedEventHandler(ProductServiceDbContext dbContext) : IDistributedEventHandler + { + public async Task HandleAsync(OrderPaidDistributedEvent @event) + { + foreach (var orderStockItem in @event.OrderStockItems) + { + var product = await dbContext.Set().FindAsync(orderStockItem.ProductId) ?? throw new Exception($"Product with id {orderStockItem.ProductId} not found"); + + product.AvailableStock -= orderStockItem.Units; + } + + await dbContext.SaveChangesAsync(); + } + } +} diff --git a/src/HelloShop.ProductService/DistributedEvents/Events/OrderPaidDistributedEvent.cs b/src/HelloShop.ProductService/DistributedEvents/Events/OrderPaidDistributedEvent.cs new file mode 100644 index 0000000..240864b --- /dev/null +++ b/src/HelloShop.ProductService/DistributedEvents/Events/OrderPaidDistributedEvent.cs @@ -0,0 +1,9 @@ +// Copyright (c) HelloShop Corporation. All rights reserved. +// See the license file in the project root for more information. + +using HelloShop.ServiceDefaults.DistributedEvents.Abstractions; + +namespace HelloShop.ProductService.DistributedEvents.Events +{ + public record OrderPaidDistributedEvent(int OrderId, IEnumerable OrderStockItems) : DistributedEvent; +}