Skip to content

Commit

Permalink
feat(TransactionalOutbox): add Transactional Outbox pattern implement…
Browse files Browse the repository at this point in the history
…ation

- Added new projects `ES.FX.TransactionalOutbox.EntityFrameworkCore` and `ES.FX.TransactionalOutbox.EntityFrameworkCore.SqlServer`.
  • Loading branch information
winromulus committed Sep 6, 2024
1 parent b1f4a56 commit 51b16de
Show file tree
Hide file tree
Showing 75 changed files with 1,855 additions and 184 deletions.
14 changes: 14 additions & 0 deletions ES.FX.sln
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ES.FX.Ignite.StackExchange.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ES.FX.Microsoft.AspNetCore", "src\ES.FX.Microsoft.AspNetCore\ES.FX.Microsoft.AspNetCore.csproj", "{3FBD2030-83AB-4D33-8E7C-9F4FCA4E9603}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ES.FX.TransactionalOutbox.EntityFrameworkCore.SqlServer", "src\ES.FX.TransactionalOutbox.EntityFrameworkCore.SqlServer\ES.FX.TransactionalOutbox.EntityFrameworkCore.SqlServer.csproj", "{9E83E1A6-0B46-4903-B1DA-16C463039E32}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ES.FX.TransactionalOutbox.EntityFrameworkCore", "src\ES.FX.TransactionalOutbox.EntityFrameworkCore\ES.FX.TransactionalOutbox.EntityFrameworkCore.csproj", "{C62E3FA6-98E6-405F-9E6B-860A38CDB864}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -444,6 +448,14 @@ Global
{3FBD2030-83AB-4D33-8E7C-9F4FCA4E9603}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3FBD2030-83AB-4D33-8E7C-9F4FCA4E9603}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3FBD2030-83AB-4D33-8E7C-9F4FCA4E9603}.Release|Any CPU.Build.0 = Release|Any CPU
{9E83E1A6-0B46-4903-B1DA-16C463039E32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E83E1A6-0B46-4903-B1DA-16C463039E32}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E83E1A6-0B46-4903-B1DA-16C463039E32}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E83E1A6-0B46-4903-B1DA-16C463039E32}.Release|Any CPU.Build.0 = Release|Any CPU
{C62E3FA6-98E6-405F-9E6B-860A38CDB864}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C62E3FA6-98E6-405F-9E6B-860A38CDB864}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C62E3FA6-98E6-405F-9E6B-860A38CDB864}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C62E3FA6-98E6-405F-9E6B-860A38CDB864}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -523,6 +535,8 @@ Global
{1A2B33E0-AF04-408C-9C33-2A4E52355564} = {32548940-0629-46AC-B9BF-E7C41F1C49DE}
{F3C2DA14-F398-4192-B4D0-21B05719637E} = {32548940-0629-46AC-B9BF-E7C41F1C49DE}
{3FBD2030-83AB-4D33-8E7C-9F4FCA4E9603} = {F8538BCE-36D3-4317-8C3C-7540117B99A7}
{9E83E1A6-0B46-4903-B1DA-16C463039E32} = {F8538BCE-36D3-4317-8C3C-7540117B99A7}
{C62E3FA6-98E6-405F-9E6B-860A38CDB864} = {F8538BCE-36D3-4317-8C3C-7540117B99A7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {388473D0-FE20-4821-BFE4-C3CD3E184C8F}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#pragma warning disable CS9113 // Parameter is unread.

using StackExchange.Redis;
using ES.FX.TransactionalOutbox.EntityFrameworkCore;
using ES.FX.TransactionalOutbox.EntityFrameworkCore.Messages;
using Microsoft.EntityFrameworkCore;
using Playground.Microservice.Api.Host.Testing;
using Playground.Shared.Data.Simple.EntityFrameworkCore;
using Playground.Shared.Data.Simple.EntityFrameworkCore.Entities;

namespace Playground.Microservice.Api.Host.HostedServices;

Expand All @@ -10,12 +15,29 @@ internal class TestHostedService(
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.CompletedTask;


var factory = serviceProvider.GetRequiredService<IDbContextFactory<SimpleDbContext>>();
while (true)
{
await Task.Delay(2000);
await using var dbContext = await factory.CreateDbContextAsync(stoppingToken);


for (var i = 0; i < 50; i++)
{
dbContext.AddOutboxMessage(new OutboxTestMessage("Property"), new OutboxMessageOptions
{
MaxAttempts = 5,
DelayBetweenAttempts = 5,
DelayBetweenAttemptsIsExponential = true
});
dbContext.SimpleUsers.Add(new SimpleUser { Id = Guid.NewGuid() });
}

await dbContext.SaveChangesAsync(stoppingToken).ConfigureAwait(false);

var multiplexer = serviceProvider.GetRequiredService<IConnectionMultiplexer>();
multiplexer.GetDatabase().StringSet("test", "test"u8.ToArray());
await Task.Delay(3_000, stoppingToken).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>..\..</DockerfileContext>
<ServerGarbageCollection>false</ServerGarbageCollection>

</PropertyGroup>

Expand All @@ -17,24 +18,33 @@

<ItemGroup>
<ProjectReference Include="..\..\src\ES.FX.Hosting\ES.FX.Hosting.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.AspNetCore.HealthChecks.UI\ES.FX.Ignite.AspNetCore.HealthChecks.UI.csproj" />
<ProjectReference
Include="..\..\src\ES.FX.Ignite.AspNetCore.HealthChecks.UI\ES.FX.Ignite.AspNetCore.HealthChecks.UI.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.Azure.Data.Tables\ES.FX.Ignite.Azure.Data.Tables.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.Azure.Security.KeyVault.Secrets\ES.FX.Ignite.Azure.Security.KeyVault.Secrets.csproj" />
<ProjectReference
Include="..\..\src\ES.FX.Ignite.Azure.Security.KeyVault.Secrets\ES.FX.Ignite.Azure.Security.KeyVault.Secrets.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.Azure.Storage.Blobs\ES.FX.Ignite.Azure.Storage.Blobs.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.Azure.Storage.Queues\ES.FX.Ignite.Azure.Storage.Queues.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.FluentValidation\ES.FX.Ignite.FluentValidation.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.Microsoft.Data.SqlClient\ES.FX.Ignite.Microsoft.Data.SqlClient.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.Microsoft.EntityFrameworkCore.SqlServer\ES.FX.Ignite.Microsoft.EntityFrameworkCore.SqlServer.csproj" />
<ProjectReference
Include="..\..\src\ES.FX.Ignite.Microsoft.Data.SqlClient\ES.FX.Ignite.Microsoft.Data.SqlClient.csproj" />
<ProjectReference
Include="..\..\src\ES.FX.Ignite.Microsoft.EntityFrameworkCore.SqlServer\ES.FX.Ignite.Microsoft.EntityFrameworkCore.SqlServer.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.Migrations\ES.FX.Ignite.Migrations.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.NSwag\ES.FX.Ignite.NSwag.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.OpenTelemetry.Exporter.Seq\ES.FX.Ignite.OpenTelemetry.Exporter.Seq.csproj" />
<ProjectReference
Include="..\..\src\ES.FX.Ignite.OpenTelemetry.Exporter.Seq\ES.FX.Ignite.OpenTelemetry.Exporter.Seq.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.Serilog\ES.FX.Ignite.Serilog.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.StackExchange.Redis\ES.FX.Ignite.StackExchange.Redis.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite.Swashbuckle\ES.FX.Ignite.Swashbuckle.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Ignite\ES.FX.Ignite.csproj" />
<ProjectReference Include="..\..\src\ES.FX.Serilog\ES.FX.Serilog.csproj" />
<ProjectReference Include="..\Playground.Shared.Data.Simple.EntityFrameworkCore.SqlServer\Playground.Shared.Data.Simple.EntityFrameworkCore.SqlServer.csproj" />
<ProjectReference Include="..\Playground.Shared.Data.Simple.EntityFrameworkCore\Playground.Shared.Data.Simple.EntityFrameworkCore.csproj" />
<ProjectReference
Include="..\..\src\ES.FX.TransactionalOutbox.EntityFrameworkCore.SqlServer\ES.FX.TransactionalOutbox.EntityFrameworkCore.SqlServer.csproj" />
<ProjectReference
Include="..\Playground.Shared.Data.Simple.EntityFrameworkCore.SqlServer\Playground.Shared.Data.Simple.EntityFrameworkCore.SqlServer.csproj" />
<ProjectReference
Include="..\Playground.Shared.Data.Simple.EntityFrameworkCore\Playground.Shared.Data.Simple.EntityFrameworkCore.csproj" />
</ItemGroup>

</Project>
14 changes: 13 additions & 1 deletion playground/Playground.Microservice.Api.Host/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
using ES.FX.Ignite.Serilog.Hosting;
using ES.FX.Ignite.StackExchange.Redis.Hosting;
using ES.FX.Serilog.Lifetime;
using ES.FX.TransactionalOutbox.EntityFrameworkCore;
using ES.FX.TransactionalOutbox.EntityFrameworkCore.SqlServer;
using FluentValidation;
using HealthChecks.UI.Client;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Playground.Microservice.Api.Host.HostedServices;
using Playground.Microservice.Api.Host.Testing;
using Playground.Shared.Data.Simple.EntityFrameworkCore;
Expand Down Expand Up @@ -46,7 +49,11 @@


//SqlServerDbContext
builder.IgniteSqlServerDbContextFactory<SimpleDbContext>(nameof(SimpleDbContext),
builder.IgniteSqlServerDbContextFactory<SimpleDbContext>(
configureDbContextOptionsBuilder: dbContextOptionsBuilder =>
{
dbContextOptionsBuilder.ConfigureWarnings(w => w.Ignore(SqlServerEventId.SavepointsDisabledBecauseOfMARS));
},
configureSqlServerDbContextOptionsBuilder: sqlServerDbContextOptionsBuilder =>
{
sqlServerDbContextOptionsBuilder.MigrationsAssembly(
Expand Down Expand Up @@ -80,6 +87,11 @@
builder.Services.AddScoped<IValidator<TestComplexRequest>, TestComplexRequestValidator>();


builder.Services.AddOutboxMessageType<OutboxTestMessage>();

builder.Services.AddOutboxDeliveryService<SimpleDbContext, OutboxMessageHandler>();


var app = builder.Build();
app.Ignite();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,18 @@
"publishAllPorts": true,
"useSSL": false,
"httpPort": 50000,
"dockerfileRunArguments": "--network localenv"
"dockerfileRunArguments": "--network localenv --memory=512m"
},
"HOST": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HTTP_PORTS": "8080"
},
"applicationUrl": "http://0.0.0.0:50001",
"dotnetRunMessages": false,
"httpPort": 50000
}
},
"$schema": "http://json.schemastore.org/launchsettings.json"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using ES.FX.TransactionalOutbox.EntityFrameworkCore.Delivery;

namespace Playground.Microservice.Api.Host.Testing;

public class OutboxMessageHandler : IOutboxMessageHandler
{
public ValueTask<bool> IsReadyAsync() => ValueTask.FromResult(true);

public async ValueTask<bool> HandleAsync(OutboxMessageHandlerContext context,
CancellationToken cancellationToken = default)
{
await Task.CompletedTask;


return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using ES.FX.TransactionalOutbox.EntityFrameworkCore.Messages;

namespace Playground.Microservice.Api.Host.Testing;

[OutboxMessageType("SomeTestMessage")]
public record OutboxTestMessage(string SomeProp);
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"MinimumLevel": {
"Default": "Verbose",
"Override": {
"ES.FX": "Verbose",
"HealthChecks.UI": "Warning",
"Azure.Core": "Warning",
"Microsoft": "Information",
Expand Down
Loading

0 comments on commit 51b16de

Please sign in to comment.