From d20e9ea3b61c0e6ed6107f354c2330070a7899b3 Mon Sep 17 00:00:00 2001 From: Philipp Theyssen <34607877+PTheyssen@users.noreply.github.com> Date: Thu, 23 Mar 2023 20:15:22 +0100 Subject: [PATCH] Add event bus events for catalog service. (#8) * Add event bus events for catalog service. * Fix logic for catalog publishing events. --- .../Controllers/CatalogController.cs | 48 +++++++++++++++++-- .../Events/ProductCreatedIntegrationEvent.cs | 39 +++++++++++++++ .../Events/ProductDeletedIntegrationEvent.cs | 11 +++++ .../ProductStockChangedIntegrationEvent.cs | 20 ++++++++ 4 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductCreatedIntegrationEvent.cs create mode 100644 src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductDeletedIntegrationEvent.cs create mode 100644 src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductStockChangedIntegrationEvent.cs diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 4dd1143f6..9c4c39fd6 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -216,13 +216,19 @@ public class CatalogController : ControllerBase } var oldPrice = catalogItem.Price; + var oldStock = catalogItem.AvailableStock; var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price; + var raiseProductStockChangedEvent = oldStock != productToUpdate.AvailableStock; // Update current product catalogItem = productToUpdate; _catalogContext.CatalogItems.Update(catalogItem); - if (raiseProductPriceChangedEvent) // Save product's data and publish integration event through the Event Bus if price has changed + if (!raiseProductPriceChangedEvent && !raiseProductStockChangedEvent)// Just save the updated product because the Product's Price hasn't changed. + { + await _catalogContext.SaveChangesAsync(); + } + else if (raiseProductPriceChangedEvent) // Save product's data and publish integration event through the Event Bus if price has changed { //Create Integration Event to be published through the Event Bus var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); @@ -233,10 +239,19 @@ public class CatalogController : ControllerBase // Publish through the Event Bus and mark the saved event as published await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent); } - else // Just save the updated product because the Product's Price hasn't changed. + else if (raiseProductStockChangedEvent) { - await _catalogContext.SaveChangesAsync(); + // Create Integration Event to be published through the Event Bus + var stockChangedEvent = new ProductStockChangedIntegrationEvent(catalogItem.Id, productToUpdate.AvailableStock, oldStock); + + // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction + await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(stockChangedEvent); + + // Publish through the Event Bus and mark the saved event as published + await _catalogIntegrationEventService.PublishThroughEventBusAsync(stockChangedEvent); + } + return CreatedAtAction(nameof(ItemByIdAsync), new { id = productToUpdate.Id }, null); } @@ -261,6 +276,26 @@ public class CatalogController : ControllerBase await _catalogContext.SaveChangesAsync(); + // publish CatalogItemCreatedIntegrationEvent + var productCreatedEvent = new ProductCreatedIntegrationEvent() + { + AvailableStock = item.AvailableStock, + CatalogBrand = item.CatalogBrand, + CatalogBrandId = item.CatalogBrandId, + CatalogType = item.CatalogType, + CatalogTypeId = item.CatalogTypeId, + Description = item.Description, + Id = item.Id, + MaxStockThreshold = item.MaxStockThreshold, + Name = item.Name, + OnReorder = item.OnReorder + }; + // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction + await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(productCreatedEvent); + + // Publish through the Event Bus and mark the saved event as published + await _catalogIntegrationEventService.PublishThroughEventBusAsync(productCreatedEvent); + return CreatedAtAction(nameof(ItemByIdAsync), new { id = item.Id }, null); } @@ -282,6 +317,13 @@ public class CatalogController : ControllerBase await _catalogContext.SaveChangesAsync(); + var productDeletedEvent = new ProductDeletedIntegrationEvent(product.Id); + // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction + await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(productDeletedEvent); + + // Publish through the Event Bus and mark the saved event as published + await _catalogIntegrationEventService.PublishThroughEventBusAsync(productDeletedEvent); + return NoContent(); } diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductCreatedIntegrationEvent.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductCreatedIntegrationEvent.cs new file mode 100644 index 000000000..a670da604 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductCreatedIntegrationEvent.cs @@ -0,0 +1,39 @@ +namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; + +public record ProductCreatedIntegrationEvent : IntegrationEvent +{ + public int Id { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + + public decimal Price { get; set; } + + public string PictureFileName { get; set; } + + public string PictureUri { get; set; } + + public int CatalogTypeId { get; set; } + + public CatalogType CatalogType { get; set; } + + public int CatalogBrandId { get; set; } + + public CatalogBrand CatalogBrand { get; set; } + + // Quantity in stock + public int AvailableStock { get; set; } + + // Available stock at which we should reorder + public int RestockThreshold { get; set; } + + + // Maximum number of units that can be in-stock at any time (due to physicial/logistical constraints in warehouses) + public int MaxStockThreshold { get; set; } + + /// + /// True if item is on reorder + /// + public bool OnReorder { get; set; } +} \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductDeletedIntegrationEvent.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductDeletedIntegrationEvent.cs new file mode 100644 index 000000000..09bc2d383 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductDeletedIntegrationEvent.cs @@ -0,0 +1,11 @@ +namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; + +public record ProductDeletedIntegrationEvent : IntegrationEvent +{ + public int ProductId { get; private init; } + + public ProductDeletedIntegrationEvent(int productId) + { + ProductId = productId; + } +} \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductStockChangedIntegrationEvent.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductStockChangedIntegrationEvent.cs new file mode 100644 index 000000000..c64f24761 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductStockChangedIntegrationEvent.cs @@ -0,0 +1,20 @@ +namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; + +// Integration Events notes: +// An Event is “something that has happened in the past”, therefore its name has to be past tense +// An Integration Event is an event that can cause side effects to other microservices, Bounded-Contexts or external systems. +public record ProductStockChangedIntegrationEvent : IntegrationEvent +{ + public int ProductId { get; private init; } + + public decimal NewStock { get; private init; } + + public decimal OldStock { get; private init; } + + public ProductStockChangedIntegrationEvent(int productId, decimal newStock, decimal oldStock) + { + ProductId = productId; + NewStock = newStock; + OldStock = oldStock; + } +} \ No newline at end of file