From d9c004a92da98da2fdd25cc246235987f7d3ce59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C3=B3n=20Tom=C3=A1s?= Date: Fri, 31 Mar 2017 10:30:56 +0200 Subject: [PATCH 1/4] Added integration event for cleaning basket when order is created --- .../OrderStartedIntegrationEventHandler.cs | 27 ++++++++++++ .../Events/OrderStartedIntegrationEvent.cs | 19 ++++++++ src/Services/Basket/Basket.API/Startup.cs | 5 +++ ...PaymentMethodVerifiedDomainEventHandler.cs | 44 ++++++++++++++++--- .../AutofacModules/MediatorModule.cs | 3 +- ...131634_IntegrationEventInitial.Designer.cs | 43 ++++++++++++++++++ .../20170330131634_IntegrationEventInitial.cs | 34 ++++++++++++++ ...IntegrationEventLogContextModelSnapshot.cs | 42 ++++++++++++++++++ .../Events/OrderStartedIntegrationEvent.cs | 19 ++++++++ .../Ordering/Ordering.API/Ordering.API.csproj | 16 +++++-- src/Services/Ordering/Ordering.API/Startup.cs | 17 ++++++- .../Ordering.Infrastructure.csproj | 5 ++- src/Web/WebMVC/Controllers/OrderController.cs | 3 -- 13 files changed, 261 insertions(+), 16 deletions(-) create mode 100644 src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs create mode 100644 src/Services/Basket/Basket.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs create mode 100644 src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/20170330131634_IntegrationEventInitial.Designer.cs create mode 100644 src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/20170330131634_IntegrationEventInitial.cs create mode 100644 src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/IntegrationEventLogContextModelSnapshot.cs create mode 100644 src/Services/Ordering/Ordering.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs new file mode 100644 index 000000000..e35badc64 --- /dev/null +++ b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs @@ -0,0 +1,27 @@ +using Basket.API.IntegrationEvents.Events; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.eShopOnContainers.Services.Basket.API.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Basket.API.IntegrationEvents.EventHandling +{ + public class OrderStartedIntegrationEventHandler : IIntegrationEventHandler + { + private readonly IBasketRepository _repository; + public OrderStartedIntegrationEventHandler(IBasketRepository repository) + { + _repository = repository; + } + + public async Task Handle(OrderStartedIntegrationEvent @event) + { + await _repository.DeleteBasketAsync(@event.UserId.ToString()); + } + } +} + + + diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs b/src/Services/Basket/Basket.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs new file mode 100644 index 000000000..3b5726e83 --- /dev/null +++ b/src/Services/Basket/Basket.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs @@ -0,0 +1,19 @@ +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Basket.API.IntegrationEvents.Events +{ + // Integration Events notes: + // An Event is “something that has happened in the past”, therefore its name has to be + // An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems. + public class OrderStartedIntegrationEvent : IntegrationEvent + { + public string UserId { get; } + + public OrderStartedIntegrationEvent(string userId) => + UserId = userId; + } +} diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index f783fccff..7d16c5691 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -17,6 +17,8 @@ using System; using Microsoft.Extensions.HealthChecks; using System.Threading.Tasks; using Basket.API.Infrastructure.Filters; +using Basket.API.IntegrationEvents.Events; +using Basket.API.IntegrationEvents.EventHandling; namespace Microsoft.eShopOnContainers.Services.Basket.API { @@ -91,6 +93,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API services.AddTransient(); services.AddTransient, ProductPriceChangedIntegrationEventHandler>(); + services.AddTransient, OrderStartedIntegrationEventHandler>(); var serviceProvider = services.BuildServiceProvider(); var configuration = serviceProvider.GetRequiredService>().Value; @@ -117,8 +120,10 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API .UseSwaggerUi(); var catalogPriceHandler = app.ApplicationServices.GetService>(); + var orderStartedHandler = app.ApplicationServices.GetService>(); var eventBus = app.ApplicationServices.GetRequiredService(); eventBus.Subscribe(catalogPriceHandler); + eventBus.Subscribe(orderStartedHandler); } diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs index 1ac863b1e..550d7055e 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs @@ -1,8 +1,15 @@ using MediatR; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; using Microsoft.Extensions.Logging; +using Ordering.API.IntegrationEvents.Events; using Ordering.Domain.Events; using System; +using System.Data.Common; using System.Threading.Tasks; namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified @@ -12,9 +19,16 @@ namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVeri { private readonly IOrderRepository _orderRepository; private readonly ILoggerFactory _logger; - public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler(IOrderRepository orderRepository, ILoggerFactory logger) + private readonly Func _integrationEventLogServiceFactory; + private readonly IEventBus _eventBus; + + public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler( + IOrderRepository orderRepository, ILoggerFactory logger, IEventBus eventBus, + Func integrationEventLogServiceFactory) { _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); + _integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory)); + _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } @@ -26,12 +40,30 @@ namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVeri var orderToUpdate = await _orderRepository.GetAsync(buyerPaymentMethodVerifiedEvent.OrderId); orderToUpdate.SetBuyerId(buyerPaymentMethodVerifiedEvent.Buyer.Id); orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id); - - await _orderRepository.UnitOfWork - .SaveEntitiesAsync(); - + + var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(buyerPaymentMethodVerifiedEvent.Buyer.IdentityGuid); + //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): + //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + var orderingContext = _orderRepository.UnitOfWork as OrderingContext; + var strategy = orderingContext.Database.CreateExecutionStrategy(); + + var eventLogService = _integrationEventLogServiceFactory(orderingContext.Database.GetDbConnection()); + await strategy.ExecuteAsync(async () => + { + // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction + using (var transaction = orderingContext.Database.BeginTransaction()) + { + await _orderRepository.UnitOfWork.SaveEntitiesAsync(); + await eventLogService.SaveEventAsync(orderStartedIntegrationEvent, orderingContext.Database.CurrentTransaction.GetDbTransaction()); + transaction.Commit(); + } + }); + _logger.CreateLogger(nameof(UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler)) .LogTrace($"Order with Id: {buyerPaymentMethodVerifiedEvent.OrderId} has been successfully updated with a payment method id: { buyerPaymentMethodVerifiedEvent.Payment.Id }"); + + _eventBus.Publish(orderStartedIntegrationEvent); + await eventLogService.MarkEventAsPublishedAsync(orderStartedIntegrationEvent); } - } + } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs index e741644f3..4f38dd59e 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs @@ -31,7 +31,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof builder.RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler).GetTypeInfo().Assembly) .As(o => o.GetInterfaces() .Where(i => i.IsClosedTypeOf(typeof(IAsyncNotificationHandler<>))) - .Select(i => new KeyedService("IAsyncNotificationHandler", i))); + .Select(i => new KeyedService("IAsyncNotificationHandler", i))) + .AsImplementedInterfaces(); builder .RegisterAssemblyTypes(typeof(CreateOrderCommandValidator).GetTypeInfo().Assembly) diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/20170330131634_IntegrationEventInitial.Designer.cs b/src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/20170330131634_IntegrationEventInitial.Designer.cs new file mode 100644 index 000000000..65b6a6e05 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/20170330131634_IntegrationEventInitial.Designer.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; + +namespace Ordering.API.Infrastructure.IntegrationEventMigrations +{ + [DbContext(typeof(IntegrationEventLogContext))] + [Migration("20170330131634_IntegrationEventInitial")] + partial class IntegrationEventInitial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.1") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.IntegrationEventLogEntry", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd(); + + b.Property("Content") + .IsRequired(); + + b.Property("CreationTime"); + + b.Property("EventTypeName") + .IsRequired(); + + b.Property("State"); + + b.Property("TimesSent"); + + b.HasKey("EventId"); + + b.ToTable("IntegrationEventLog"); + }); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/20170330131634_IntegrationEventInitial.cs b/src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/20170330131634_IntegrationEventInitial.cs new file mode 100644 index 000000000..9830ebf7b --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/20170330131634_IntegrationEventInitial.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ordering.API.Infrastructure.IntegrationEventMigrations +{ + public partial class IntegrationEventInitial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "IntegrationEventLog", + columns: table => new + { + EventId = table.Column(nullable: false), + Content = table.Column(nullable: false), + CreationTime = table.Column(nullable: false), + EventTypeName = table.Column(nullable: false), + State = table.Column(nullable: false), + TimesSent = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IntegrationEventLog", x => x.EventId); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "IntegrationEventLog"); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/IntegrationEventLogContextModelSnapshot.cs b/src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/IntegrationEventLogContextModelSnapshot.cs new file mode 100644 index 000000000..29bd24729 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/IntegrationEventLogContextModelSnapshot.cs @@ -0,0 +1,42 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; + +namespace Ordering.API.Infrastructure.IntegrationEventMigrations +{ + [DbContext(typeof(IntegrationEventLogContext))] + partial class IntegrationEventLogContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.1") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.IntegrationEventLogEntry", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd(); + + b.Property("Content") + .IsRequired(); + + b.Property("CreationTime"); + + b.Property("EventTypeName") + .IsRequired(); + + b.Property("State"); + + b.Property("TimesSent"); + + b.HasKey("EventId"); + + b.ToTable("IntegrationEventLog"); + }); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs new file mode 100644 index 000000000..8184483e5 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs @@ -0,0 +1,19 @@ +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Ordering.API.IntegrationEvents.Events +{ + // Integration Events notes: + // An Event is “something that has happened in the past”, therefore its name has to be + // An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems. + public class OrderStartedIntegrationEvent : IntegrationEvent + { + public string UserId { get; } + + public OrderStartedIntegrationEvent(string userId) => + UserId = userId; + } +} diff --git a/src/Services/Ordering/Ordering.API/Ordering.API.csproj b/src/Services/Ordering/Ordering.API/Ordering.API.csproj index 5d9f6b258..6eed3a13c 100644 --- a/src/Services/Ordering/Ordering.API/Ordering.API.csproj +++ b/src/Services/Ordering/Ordering.API/Ordering.API.csproj @@ -23,6 +23,9 @@ + + + @@ -38,16 +41,18 @@ + + - + - - + + @@ -73,4 +78,9 @@ + + + + + diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index 2875eb2de..b7d90fa81 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -12,12 +12,17 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; + using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; + using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.Logging; using Ordering.Infrastructure; using System; + using System.Data.Common; using System.Reflection; public class Startup @@ -55,7 +60,7 @@ { checks.AddSqlCheck("Ordering_Db", Configuration["ConnectionString"]); }); - + services.AddEntityFrameworkSqlServer() .AddDbContext(options => { @@ -95,7 +100,11 @@ // Add application services. services.AddSingleton(); services.AddTransient(); + services.AddTransient>( + sp => (DbConnection c) => new IntegrationEventLogService(c)); + var serviceProvider = services.BuildServiceProvider(); + services.AddSingleton(new EventBusRabbitMQ(Configuration["EventBusConnection"])); services.AddOptions(); //configure autofac @@ -126,6 +135,12 @@ .UseSwaggerUi(); OrderingContextSeed.SeedAsync(app).Wait(); + + var integrationEventLogContext = new IntegrationEventLogContext( + new DbContextOptionsBuilder() + .UseSqlServer(Configuration["ConnectionString"], b => b.MigrationsAssembly("Ordering.API")) + .Options); + integrationEventLogContext.Database.Migrate(); } protected virtual void ConfigureAuth(IApplicationBuilder app) diff --git a/src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj b/src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj index 4ec458dbd..bb756e552 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj +++ b/src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj @@ -16,8 +16,9 @@ - - + + + diff --git a/src/Web/WebMVC/Controllers/OrderController.cs b/src/Web/WebMVC/Controllers/OrderController.cs index 104ae9f9b..2755282a3 100644 --- a/src/Web/WebMVC/Controllers/OrderController.cs +++ b/src/Web/WebMVC/Controllers/OrderController.cs @@ -45,9 +45,6 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers var user = _appUserParser.Parse(HttpContext.User); await _orderSvc.CreateOrder(model); - //Empty basket for current user. - await _basketSvc.CleanBasket(user); - //Redirect to historic list. return RedirectToAction("Index"); } From 3c909ff392227954a533febb97306175f76f27c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C3=B3n=20Tom=C3=A1s?= Date: Mon, 3 Apr 2017 13:13:40 +0200 Subject: [PATCH 2/4] Refactor transaction and publishing in integration events --- .../Utilities/ResilientTransaction.cs | 37 +++++++++++ .../Controllers/CatalogController.cs | 66 ++++++------------- .../CatalogIntegrationEventService.cs | 48 ++++++++++++++ .../ICatalogIntegrationEventService.cs | 14 ++++ src/Services/Catalog/Catalog.API/Startup.cs | 5 +- ...PaymentMethodVerifiedDomainEventHandler.cs | 47 +++++-------- .../IOrderingIntegrationEventService.cs | 14 ++++ .../OrderingIntegrationEventService.cs | 49 ++++++++++++++ src/Services/Ordering/Ordering.API/Startup.cs | 5 +- 9 files changed, 203 insertions(+), 82 deletions(-) create mode 100644 src/BuildingBlocks/EventBus/IntegrationEventLogEF/Utilities/ResilientTransaction.cs create mode 100644 src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs create mode 100644 src/Services/Catalog/Catalog.API/IntegrationEvents/ICatalogIntegrationEventService.cs create mode 100644 src/Services/Ordering/Ordering.API/IntegrationEvents/IOrderingIntegrationEventService.cs create mode 100644 src/Services/Ordering/Ordering.API/IntegrationEvents/OrderingIntegrationEventService.cs diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Utilities/ResilientTransaction.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Utilities/ResilientTransaction.cs new file mode 100644 index 000000000..f8227882b --- /dev/null +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Utilities/ResilientTransaction.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities +{ + public class ResilientTransaction + { + private DbContext _context; + private ResilientTransaction(DbContext context) => + _context = context ?? throw new ArgumentNullException(nameof(context)); + + public static ResilientTransaction New (DbContext context) => + new ResilientTransaction(context); + + public async Task ExecuteAsync(Func action) + { + //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): + //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + var strategy = _context.Database.CreateExecutionStrategy(); + await strategy.ExecuteAsync(async () => + { + using (var transaction = _context.Database.BeginTransaction()) + { + await action(); + transaction.Commit(); + } + }); + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index f4864f2cd..e556376d9 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -15,6 +15,8 @@ using System.Data.Common; using System.Linq; using System.Threading.Tasks; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities; +using Catalog.API.IntegrationEvents; namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers { @@ -23,15 +25,13 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers { private readonly CatalogContext _catalogContext; private readonly IOptionsSnapshot _settings; - private readonly IEventBus _eventBus; - private readonly Func _integrationEventLogServiceFactory; + private readonly ICatalogIntegrationEventService _catalogIntegrationEventService; - public CatalogController(CatalogContext Context, IOptionsSnapshot settings, IEventBus eventBus, Func integrationEventLogServiceFactory) + public CatalogController(CatalogContext Context, IOptionsSnapshot settings, ICatalogIntegrationEventService catalogIntegrationEventService) { _catalogContext = Context; + _catalogIntegrationEventService = catalogIntegrationEventService; _settings = settings; - _eventBus = eventBus; - _integrationEventLogServiceFactory = integrationEventLogServiceFactory; ((DbContext)Context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } @@ -145,51 +145,25 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers { var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id); if (catalogItem == null) return NotFound(); - - bool raiseProductPriceChangedEvent = false; - IntegrationEvent priceChangedEvent = null; - - if (catalogItem.Price != productToUpdate.Price) raiseProductPriceChangedEvent = true; - - if (raiseProductPriceChangedEvent) // Create event if price has changed - { - var oldPrice = catalogItem.Price; - priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); - } - - //Update current product + var raiseProductPriceChangedEvent = catalogItem.Price != productToUpdate.Price; + var oldPrice = catalogItem.Price; + + // Update current product catalogItem = productToUpdate; + _catalogContext.CatalogItems.Update(catalogItem); - //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): - //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - var strategy = _catalogContext.Database.CreateExecutionStrategy(); - var eventLogService = _integrationEventLogServiceFactory(_catalogContext.Database.GetDbConnection()); - await strategy.ExecuteAsync(async () => - { + if (raiseProductPriceChangedEvent) // Save and publish event if price has changed + { + var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction - using (var transaction = _catalogContext.Database.BeginTransaction()) - { - _catalogContext.CatalogItems.Update(catalogItem); - await _catalogContext.SaveChangesAsync(); - - //Save to EventLog only if product price changed - if (raiseProductPriceChangedEvent) - { - - await eventLogService.SaveEventAsync(priceChangedEvent, _catalogContext.Database.CurrentTransaction.GetDbTransaction()); - } - - transaction.Commit(); - } - }); - - - //Publish to Event Bus only if product price changed - if (raiseProductPriceChangedEvent) - { - _eventBus.Publish(priceChangedEvent); - await eventLogService.MarkEventAsPublishedAsync(priceChangedEvent); + await _catalogIntegrationEventService.SaveEventAsync(priceChangedEvent); + // Publish to Event Bus only if product price changed + await _catalogIntegrationEventService.PublishAsync(priceChangedEvent); } + else // Save updated product + { + await _catalogContext.SaveChangesAsync(); + } return Ok(); } diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs new file mode 100644 index 000000000..d751d155b --- /dev/null +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities; +using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; +using System; +using System.Data.Common; +using System.Threading.Tasks; + +namespace Catalog.API.IntegrationEvents +{ + public class CatalogIntegrationEventService : ICatalogIntegrationEventService + { + private readonly Func _integrationEventLogServiceFactory; + private readonly IEventBus _eventBus; + private readonly CatalogContext _catalogContext; + private readonly IIntegrationEventLogService _eventLogService; + + public CatalogIntegrationEventService(IEventBus eventBus, CatalogContext catalogContext, + Func integrationEventLogServiceFactory) + { + _catalogContext = catalogContext ?? throw new ArgumentNullException(nameof(catalogContext)); + _integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory)); + _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); + _eventLogService = _integrationEventLogServiceFactory(_catalogContext.Database.GetDbConnection()); + } + + public async Task PublishAsync(IntegrationEvent evt) + { + _eventBus.Publish(evt); + await _eventLogService.MarkEventAsPublishedAsync(evt); + } + + public async Task SaveEventAsync(IntegrationEvent evt) + { + //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): + //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + await ResilientTransaction.New(_catalogContext) + .ExecuteAsync(async () => { + // Achieving atomicity between original catalog database operation and the IntegrationEventLog thanks to a local transaction + await _catalogContext.SaveChangesAsync(); + await _eventLogService.SaveEventAsync(evt, _catalogContext.Database.CurrentTransaction.GetDbTransaction()); + }); + } + } +} diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/ICatalogIntegrationEventService.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/ICatalogIntegrationEventService.cs new file mode 100644 index 000000000..1e695e692 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/ICatalogIntegrationEventService.cs @@ -0,0 +1,14 @@ +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Catalog.API.IntegrationEvents +{ + public interface ICatalogIntegrationEventService + { + Task SaveEventAsync(IntegrationEvent evt); + Task PublishAsync(IntegrationEvent evt); + } +} diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index d49cc20be..716376e67 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -20,6 +20,7 @@ using System.IO; using System.Data.Common; using System.Reflection; + using global::Catalog.API.IntegrationEvents; public class Startup { @@ -97,10 +98,10 @@ }); services.AddTransient>( - sp => (DbConnection c) => new IntegrationEventLogService(c)); - + sp => (DbConnection c) => new IntegrationEventLogService(c)); var serviceProvider = services.BuildServiceProvider(); var configuration = serviceProvider.GetRequiredService>().Value; + services.AddTransient(); services.AddSingleton(new EventBusRabbitMQ(configuration.EventBusConnection)); } diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs index 550d7055e..dd469ae78 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs @@ -1,15 +1,10 @@ using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; using Microsoft.Extensions.Logging; +using Ordering.API.IntegrationEvents; using Ordering.API.IntegrationEvents.Events; using Ordering.Domain.Events; using System; -using System.Data.Common; using System.Threading.Tasks; namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified @@ -18,17 +13,15 @@ namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVeri : IAsyncNotificationHandler { private readonly IOrderRepository _orderRepository; - private readonly ILoggerFactory _logger; - private readonly Func _integrationEventLogServiceFactory; - private readonly IEventBus _eventBus; + private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; + private readonly ILoggerFactory _logger; public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler( - IOrderRepository orderRepository, ILoggerFactory logger, IEventBus eventBus, - Func integrationEventLogServiceFactory) + IOrderRepository orderRepository, ILoggerFactory logger, + IOrderingIntegrationEventService orderingIntegrationEventService) { _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); - _integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory)); - _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); + _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } @@ -42,28 +35,18 @@ namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVeri orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id); var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(buyerPaymentMethodVerifiedEvent.Buyer.IdentityGuid); - //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): - //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - var orderingContext = _orderRepository.UnitOfWork as OrderingContext; - var strategy = orderingContext.Database.CreateExecutionStrategy(); - var eventLogService = _integrationEventLogServiceFactory(orderingContext.Database.GetDbConnection()); - await strategy.ExecuteAsync(async () => - { - // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction - using (var transaction = orderingContext.Database.BeginTransaction()) - { - await _orderRepository.UnitOfWork.SaveEntitiesAsync(); - await eventLogService.SaveEventAsync(orderStartedIntegrationEvent, orderingContext.Database.CurrentTransaction.GetDbTransaction()); - transaction.Commit(); - } - }); + // Using a local transaction to achieve atomicity between original Ordering database operation and + // the IntegrationEventLog. Only saving event if order has been successfully persisted to db + await _orderingIntegrationEventService + .SaveEventAsync(orderStartedIntegrationEvent); - _logger.CreateLogger(nameof(UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler)) - .LogTrace($"Order with Id: {buyerPaymentMethodVerifiedEvent.OrderId} has been successfully updated with a payment method id: { buyerPaymentMethodVerifiedEvent.Payment.Id }"); + // Publish ordering integration event and mark it as published + await _orderingIntegrationEventService + .PublishAsync(orderStartedIntegrationEvent); - _eventBus.Publish(orderStartedIntegrationEvent); - await eventLogService.MarkEventAsPublishedAsync(orderStartedIntegrationEvent); + _logger.CreateLogger(nameof(UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler)) + .LogTrace($"Order with Id: {buyerPaymentMethodVerifiedEvent.OrderId} has been successfully updated with a payment method id: { buyerPaymentMethodVerifiedEvent.Payment.Id }"); } } } diff --git a/src/Services/Ordering/Ordering.API/IntegrationEvents/IOrderingIntegrationEventService.cs b/src/Services/Ordering/Ordering.API/IntegrationEvents/IOrderingIntegrationEventService.cs new file mode 100644 index 000000000..93ea518ba --- /dev/null +++ b/src/Services/Ordering/Ordering.API/IntegrationEvents/IOrderingIntegrationEventService.cs @@ -0,0 +1,14 @@ +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Ordering.API.IntegrationEvents +{ + public interface IOrderingIntegrationEventService + { + Task SaveEventAsync(IntegrationEvent evt); + Task PublishAsync(IntegrationEvent evt); + } +} diff --git a/src/Services/Ordering/Ordering.API/IntegrationEvents/OrderingIntegrationEventService.cs b/src/Services/Ordering/Ordering.API/IntegrationEvents/OrderingIntegrationEventService.cs new file mode 100644 index 000000000..b05ea44d0 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/IntegrationEvents/OrderingIntegrationEventService.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +using System; +using System.Data.Common; +using System.Threading.Tasks; + +namespace Ordering.API.IntegrationEvents +{ + public class OrderingIntegrationEventService : IOrderingIntegrationEventService + { + private readonly Func _integrationEventLogServiceFactory; + private readonly IEventBus _eventBus; + private readonly OrderingContext _orderingContext; + private readonly IIntegrationEventLogService _eventLogService; + + public OrderingIntegrationEventService (IEventBus eventBus, OrderingContext orderingContext, + Func integrationEventLogServiceFactory) + { + _orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext)); + _integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory)); + _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); + _eventLogService = _integrationEventLogServiceFactory(_orderingContext.Database.GetDbConnection()); + } + + public async Task PublishAsync(IntegrationEvent evt) + { + _eventBus.Publish(evt); + await _eventLogService.MarkEventAsPublishedAsync(evt); + } + + public async Task SaveEventAsync(IntegrationEvent evt) + { + //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): + //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + await ResilientTransaction.New(_orderingContext) + .ExecuteAsync(async () => { + // Achieving atomicity between original ordering database operation and the IntegrationEventLog thanks to a local transaction + await _orderingContext.SaveChangesAsync(); + await _eventLogService.SaveEventAsync(evt, _orderingContext.Database.CurrentTransaction.GetDbTransaction()); + }); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index b7d90fa81..0c7f9ac73 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -4,6 +4,7 @@ using Autofac; using Autofac.Extensions.DependencyInjection; using global::Ordering.API.Infrastructure.Middlewares; + using global::Ordering.API.IntegrationEvents; using Infrastructure; using Infrastructure.Auth; using Infrastructure.AutofacModules; @@ -101,9 +102,9 @@ services.AddSingleton(); services.AddTransient(); services.AddTransient>( - sp => (DbConnection c) => new IntegrationEventLogService(c)); - + sp => (DbConnection c) => new IntegrationEventLogService(c)); var serviceProvider = services.BuildServiceProvider(); + services.AddTransient(); services.AddSingleton(new EventBusRabbitMQ(Configuration["EventBusConnection"])); services.AddOptions(); From 393b47fa936dc28964829e0e7a679a73fb6c01b9 Mon Sep 17 00:00:00 2001 From: Cesar De la Torre Date: Tue, 4 Apr 2017 17:01:58 -0700 Subject: [PATCH 3/4] CatalogIntegrationEventService Refactored --- .../Catalog.API/Controllers/CatalogController.cs | 13 ++++++++----- .../CatalogIntegrationEventService.cs | 4 ++-- .../ICatalogIntegrationEventService.cs | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index e556376d9..83a2de02d 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -152,13 +152,16 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers catalogItem = productToUpdate; _catalogContext.CatalogItems.Update(catalogItem); - if (raiseProductPriceChangedEvent) // Save and publish event if price has changed - { + if (raiseProductPriceChangedEvent) // Save and publish integration event if price has changed + { + //Create Integration Event to be published through the Event Bus var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); + // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction - await _catalogIntegrationEventService.SaveEventAsync(priceChangedEvent); - // Publish to Event Bus only if product price changed - await _catalogIntegrationEventService.PublishAsync(priceChangedEvent); + await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(priceChangedEvent); + + // Publish through the Event Bus and mark the saved event as published + await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent); } else // Save updated product { diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs index d751d155b..e6e48c54b 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs @@ -27,13 +27,13 @@ namespace Catalog.API.IntegrationEvents _eventLogService = _integrationEventLogServiceFactory(_catalogContext.Database.GetDbConnection()); } - public async Task PublishAsync(IntegrationEvent evt) + public async Task PublishThroughEventBusAsync(IntegrationEvent evt) { _eventBus.Publish(evt); await _eventLogService.MarkEventAsPublishedAsync(evt); } - public async Task SaveEventAsync(IntegrationEvent evt) + public async Task SaveEventAndCatalogContextChangesAsync(IntegrationEvent evt) { //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/ICatalogIntegrationEventService.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/ICatalogIntegrationEventService.cs index 1e695e692..bb958eeaa 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/ICatalogIntegrationEventService.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/ICatalogIntegrationEventService.cs @@ -8,7 +8,7 @@ namespace Catalog.API.IntegrationEvents { public interface ICatalogIntegrationEventService { - Task SaveEventAsync(IntegrationEvent evt); - Task PublishAsync(IntegrationEvent evt); + Task SaveEventAndCatalogContextChangesAsync(IntegrationEvent evt); + Task PublishThroughEventBusAsync(IntegrationEvent evt); } } From 00f7226f30011693969a6f21a94d0e4d589180d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C3=B3n=20Tom=C3=A1s?= Date: Wed, 5 Apr 2017 11:07:26 +0200 Subject: [PATCH 4/4] Rename OrderingIntegrationEvent service methods Remove clean basket instruction from SPA client --- ...rWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs | 4 ++-- .../IntegrationEvents/IOrderingIntegrationEventService.cs | 4 ++-- .../IntegrationEvents/OrderingIntegrationEventService.cs | 4 ++-- .../modules/orders/orders-new/orders-new.component.ts | 7 ++----- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs index dd469ae78..ce55fd0c9 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs @@ -39,11 +39,11 @@ namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVeri // Using a local transaction to achieve atomicity between original Ordering database operation and // the IntegrationEventLog. Only saving event if order has been successfully persisted to db await _orderingIntegrationEventService - .SaveEventAsync(orderStartedIntegrationEvent); + .SaveEventAndOrderingContextChangesAsync(orderStartedIntegrationEvent); // Publish ordering integration event and mark it as published await _orderingIntegrationEventService - .PublishAsync(orderStartedIntegrationEvent); + .PublishThroughEventBusAsync(orderStartedIntegrationEvent); _logger.CreateLogger(nameof(UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler)) .LogTrace($"Order with Id: {buyerPaymentMethodVerifiedEvent.OrderId} has been successfully updated with a payment method id: { buyerPaymentMethodVerifiedEvent.Payment.Id }"); diff --git a/src/Services/Ordering/Ordering.API/IntegrationEvents/IOrderingIntegrationEventService.cs b/src/Services/Ordering/Ordering.API/IntegrationEvents/IOrderingIntegrationEventService.cs index 93ea518ba..6538afbfd 100644 --- a/src/Services/Ordering/Ordering.API/IntegrationEvents/IOrderingIntegrationEventService.cs +++ b/src/Services/Ordering/Ordering.API/IntegrationEvents/IOrderingIntegrationEventService.cs @@ -8,7 +8,7 @@ namespace Ordering.API.IntegrationEvents { public interface IOrderingIntegrationEventService { - Task SaveEventAsync(IntegrationEvent evt); - Task PublishAsync(IntegrationEvent evt); + Task SaveEventAndOrderingContextChangesAsync(IntegrationEvent evt); + Task PublishThroughEventBusAsync(IntegrationEvent evt); } } diff --git a/src/Services/Ordering/Ordering.API/IntegrationEvents/OrderingIntegrationEventService.cs b/src/Services/Ordering/Ordering.API/IntegrationEvents/OrderingIntegrationEventService.cs index b05ea44d0..810c07bc9 100644 --- a/src/Services/Ordering/Ordering.API/IntegrationEvents/OrderingIntegrationEventService.cs +++ b/src/Services/Ordering/Ordering.API/IntegrationEvents/OrderingIntegrationEventService.cs @@ -28,13 +28,13 @@ namespace Ordering.API.IntegrationEvents _eventLogService = _integrationEventLogServiceFactory(_orderingContext.Database.GetDbConnection()); } - public async Task PublishAsync(IntegrationEvent evt) + public async Task PublishThroughEventBusAsync(IntegrationEvent evt) { _eventBus.Publish(evt); await _eventLogService.MarkEventAsPublishedAsync(evt); } - public async Task SaveEventAsync(IntegrationEvent evt) + public async Task SaveEventAndOrderingContextChangesAsync(IntegrationEvent evt) { //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency diff --git a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts index 72114631f..8f8a10eb0 100644 --- a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts +++ b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts @@ -18,7 +18,7 @@ export class OrdersNewComponent implements OnInit { private errorReceived: Boolean; private order: IOrder; - constructor(private service: OrdersService, fb: FormBuilder, private router: Router, private basketEvents: BasketWrapperService) { + constructor(private service: OrdersService, fb: FormBuilder, private router: Router) { // Obtain user profile information this.order = service.mapBasketAndIdentityInfoNewOrder(); this.newOrderForm = fb.group({ @@ -54,10 +54,7 @@ export class OrdersNewComponent implements OnInit { return Observable.throw(errMessage); }) .subscribe(res => { - // this will emit an observable. Basket service is subscribed to this observable, and will react deleting the basket for the current user. - this.basketEvents.orderCreated(); - - this.router.navigate(['orders']); + this.router.navigate(['orders']); }); this.errorReceived = false; this.isOrderProcessing = true;