Skip to content

Commit d9c004a

Browse files
committed
Added integration event for cleaning basket when order is created
1 parent ba71b19 commit d9c004a

13 files changed

Lines changed: 261 additions & 16 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using Basket.API.IntegrationEvents.Events;
2+
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
3+
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Threading.Tasks;
8+
9+
namespace Basket.API.IntegrationEvents.EventHandling
10+
{
11+
public class OrderStartedIntegrationEventHandler : IIntegrationEventHandler<OrderStartedIntegrationEvent>
12+
{
13+
private readonly IBasketRepository _repository;
14+
public OrderStartedIntegrationEventHandler(IBasketRepository repository)
15+
{
16+
_repository = repository;
17+
}
18+
19+
public async Task Handle(OrderStartedIntegrationEvent @event)
20+
{
21+
await _repository.DeleteBasketAsync(@event.UserId.ToString());
22+
}
23+
}
24+
}
25+
26+
27+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Threading.Tasks;
6+
7+
namespace Basket.API.IntegrationEvents.Events
8+
{
9+
// Integration Events notes:
10+
// An Event is “something that has happened in the past”, therefore its name has to be
11+
// An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems.
12+
public class OrderStartedIntegrationEvent : IntegrationEvent
13+
{
14+
public string UserId { get; }
15+
16+
public OrderStartedIntegrationEvent(string userId) =>
17+
UserId = userId;
18+
}
19+
}

src/Services/Basket/Basket.API/Startup.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
using Microsoft.Extensions.HealthChecks;
1818
using System.Threading.Tasks;
1919
using Basket.API.Infrastructure.Filters;
20+
using Basket.API.IntegrationEvents.Events;
21+
using Basket.API.IntegrationEvents.EventHandling;
2022

2123
namespace Microsoft.eShopOnContainers.Services.Basket.API
2224
{
@@ -91,6 +93,7 @@ public void ConfigureServices(IServiceCollection services)
9193

9294
services.AddTransient<IBasketRepository, RedisBasketRepository>();
9395
services.AddTransient<IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>, ProductPriceChangedIntegrationEventHandler>();
96+
services.AddTransient<IIntegrationEventHandler<OrderStartedIntegrationEvent>, OrderStartedIntegrationEventHandler>();
9497

9598
var serviceProvider = services.BuildServiceProvider();
9699
var configuration = serviceProvider.GetRequiredService<IOptionsSnapshot<BasketSettings>>().Value;
@@ -117,8 +120,10 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF
117120
.UseSwaggerUi();
118121

119122
var catalogPriceHandler = app.ApplicationServices.GetService<IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>>();
123+
var orderStartedHandler = app.ApplicationServices.GetService<IIntegrationEventHandler<OrderStartedIntegrationEvent>>();
120124
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
121125
eventBus.Subscribe<ProductPriceChangedIntegrationEvent>(catalogPriceHandler);
126+
eventBus.Subscribe<OrderStartedIntegrationEvent>(orderStartedHandler);
122127

123128
}
124129

Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
using MediatR;
2+
using Microsoft.EntityFrameworkCore;
3+
using Microsoft.EntityFrameworkCore.Storage;
4+
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
5+
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
26
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
7+
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
38
using Microsoft.Extensions.Logging;
9+
using Ordering.API.IntegrationEvents.Events;
410
using Ordering.Domain.Events;
511
using System;
12+
using System.Data.Common;
613
using System.Threading.Tasks;
714

815
namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified
@@ -12,9 +19,16 @@ public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler
1219
{
1320
private readonly IOrderRepository _orderRepository;
1421
private readonly ILoggerFactory _logger;
15-
public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler(IOrderRepository orderRepository, ILoggerFactory logger)
22+
private readonly Func<DbConnection, IIntegrationEventLogService> _integrationEventLogServiceFactory;
23+
private readonly IEventBus _eventBus;
24+
25+
public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler(
26+
IOrderRepository orderRepository, ILoggerFactory logger, IEventBus eventBus,
27+
Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory)
1628
{
1729
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
30+
_integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory));
31+
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
1832
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
1933
}
2034

@@ -26,12 +40,30 @@ public async Task Handle(BuyerAndPaymentMethodVerifiedDomainEvent buyerPaymentMe
2640
var orderToUpdate = await _orderRepository.GetAsync(buyerPaymentMethodVerifiedEvent.OrderId);
2741
orderToUpdate.SetBuyerId(buyerPaymentMethodVerifiedEvent.Buyer.Id);
2842
orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id);
29-
30-
await _orderRepository.UnitOfWork
31-
.SaveEntitiesAsync();
32-
43+
44+
var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(buyerPaymentMethodVerifiedEvent.Buyer.IdentityGuid);
45+
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
46+
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
47+
var orderingContext = _orderRepository.UnitOfWork as OrderingContext;
48+
var strategy = orderingContext.Database.CreateExecutionStrategy();
49+
50+
var eventLogService = _integrationEventLogServiceFactory(orderingContext.Database.GetDbConnection());
51+
await strategy.ExecuteAsync(async () =>
52+
{
53+
// Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
54+
using (var transaction = orderingContext.Database.BeginTransaction())
55+
{
56+
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
57+
await eventLogService.SaveEventAsync(orderStartedIntegrationEvent, orderingContext.Database.CurrentTransaction.GetDbTransaction());
58+
transaction.Commit();
59+
}
60+
});
61+
3362
_logger.CreateLogger(nameof(UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler))
3463
.LogTrace($"Order with Id: {buyerPaymentMethodVerifiedEvent.OrderId} has been successfully updated with a payment method id: { buyerPaymentMethodVerifiedEvent.Payment.Id }");
64+
65+
_eventBus.Publish(orderStartedIntegrationEvent);
66+
await eventLogService.MarkEventAsPublishedAsync(orderStartedIntegrationEvent);
3567
}
36-
}
68+
}
3769
}

src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ protected override void Load(ContainerBuilder builder)
3131
builder.RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler).GetTypeInfo().Assembly)
3232
.As(o => o.GetInterfaces()
3333
.Where(i => i.IsClosedTypeOf(typeof(IAsyncNotificationHandler<>)))
34-
.Select(i => new KeyedService("IAsyncNotificationHandler", i)));
34+
.Select(i => new KeyedService("IAsyncNotificationHandler", i)))
35+
.AsImplementedInterfaces();
3536

3637
builder
3738
.RegisterAssemblyTypes(typeof(CreateOrderCommandValidator).GetTypeInfo().Assembly)

src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/20170330131634_IntegrationEventInitial.Designer.cs

Lines changed: 43 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.EntityFrameworkCore.Migrations;
4+
5+
namespace Ordering.API.Infrastructure.IntegrationEventMigrations
6+
{
7+
public partial class IntegrationEventInitial : Migration
8+
{
9+
protected override void Up(MigrationBuilder migrationBuilder)
10+
{
11+
migrationBuilder.CreateTable(
12+
name: "IntegrationEventLog",
13+
columns: table => new
14+
{
15+
EventId = table.Column<Guid>(nullable: false),
16+
Content = table.Column<string>(nullable: false),
17+
CreationTime = table.Column<DateTime>(nullable: false),
18+
EventTypeName = table.Column<string>(nullable: false),
19+
State = table.Column<int>(nullable: false),
20+
TimesSent = table.Column<int>(nullable: false)
21+
},
22+
constraints: table =>
23+
{
24+
table.PrimaryKey("PK_IntegrationEventLog", x => x.EventId);
25+
});
26+
}
27+
28+
protected override void Down(MigrationBuilder migrationBuilder)
29+
{
30+
migrationBuilder.DropTable(
31+
name: "IntegrationEventLog");
32+
}
33+
}
34+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using Microsoft.EntityFrameworkCore;
3+
using Microsoft.EntityFrameworkCore.Infrastructure;
4+
using Microsoft.EntityFrameworkCore.Metadata;
5+
using Microsoft.EntityFrameworkCore.Migrations;
6+
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
7+
8+
namespace Ordering.API.Infrastructure.IntegrationEventMigrations
9+
{
10+
[DbContext(typeof(IntegrationEventLogContext))]
11+
partial class IntegrationEventLogContextModelSnapshot : ModelSnapshot
12+
{
13+
protected override void BuildModel(ModelBuilder modelBuilder)
14+
{
15+
modelBuilder
16+
.HasAnnotation("ProductVersion", "1.1.1")
17+
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
18+
19+
modelBuilder.Entity("Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.IntegrationEventLogEntry", b =>
20+
{
21+
b.Property<Guid>("EventId")
22+
.ValueGeneratedOnAdd();
23+
24+
b.Property<string>("Content")
25+
.IsRequired();
26+
27+
b.Property<DateTime>("CreationTime");
28+
29+
b.Property<string>("EventTypeName")
30+
.IsRequired();
31+
32+
b.Property<int>("State");
33+
34+
b.Property<int>("TimesSent");
35+
36+
b.HasKey("EventId");
37+
38+
b.ToTable("IntegrationEventLog");
39+
});
40+
}
41+
}
42+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Threading.Tasks;
6+
7+
namespace Ordering.API.IntegrationEvents.Events
8+
{
9+
// Integration Events notes:
10+
// An Event is “something that has happened in the past”, therefore its name has to be
11+
// An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems.
12+
public class OrderStartedIntegrationEvent : IntegrationEvent
13+
{
14+
public string UserId { get; }
15+
16+
public OrderStartedIntegrationEvent(string userId) =>
17+
UserId = userId;
18+
}
19+
}

src/Services/Ordering/Ordering.API/Ordering.API.csproj

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
</ItemGroup>
2424

2525
<ItemGroup>
26+
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
27+
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
28+
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
2629
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
2730
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
2831
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
@@ -38,16 +41,18 @@
3841
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.0" />
3942
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.0" />
4043
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.0.2" />
44+
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.1" />
45+
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.0" />
4146
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.0" />
4247
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.0" />
4348
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.0" />
4449
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.0" />
45-
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.0" />
50+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.1" />
4651
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.0" />
4752
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.0" />
4853
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.0" />
49-
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.0" />
50-
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.0" />
54+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.1" />
55+
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.1" />
5156
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.0" />
5257
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.0" />
5358
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.1.0" />
@@ -73,4 +78,9 @@
7378
</None>
7479
</ItemGroup>
7580

81+
<ItemGroup>
82+
<Folder Include="Infrastructure\IntegrationEventMigrations\" />
83+
<Folder Include="IntegrationEvents\EventHandling\" />
84+
</ItemGroup>
85+
7686
</Project>

0 commit comments

Comments
 (0)