diff --git a/wolverine/a-frame-architecture/FinancialApi.Application.UnitTests/ApplyFeeHandlerTests.cs b/wolverine/a-frame-architecture/FinancialApi.Application.UnitTests/ApplyFeeHandlerTests.cs index 15b2ec4..f89a191 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application.UnitTests/ApplyFeeHandlerTests.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application.UnitTests/ApplyFeeHandlerTests.cs @@ -25,10 +25,15 @@ public class ApplyFeeHandlerTests // Assert using var _ = new AssertionScope(); - intents.CustomerWrite.Entity.Balance.Should().Be(account.Balance - command.Amount); - intents.RevenueWrite.Entity.Balance.Should().Be(revenueAccount.Balance + command.Amount); - intents.JournalWrite.Entity.Lines.Count.Should().BeGreaterThanOrEqualTo(2); - intents.Event.AccountId.Should().Be(account.Id); - intents.Event.Amount.Should().Be(command.Amount); + intents.CustomerWrite.Entity.Balance.Should() + .Be(account.Balance - command.Amount); + intents.RevenueWrite.Entity.Balance.Should() + .Be(revenueAccount.Balance + command.Amount); + intents.JournalWrite.Entity.Lines.Count.Should() + .BeGreaterThanOrEqualTo(2); + intents.Event.AccountId.Should() + .Be(account.Id); + intents.Event.Amount.Should() + .Be(command.Amount); } } \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ApplyFeeHandler.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ApplyFeeHandler.cs index b7719cd..88447fd 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ApplyFeeHandler.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ApplyFeeHandler.cs @@ -11,9 +11,7 @@ namespace FinancialApi.Application.Handlers; public static class ApplyFeeHandler { - public static async Task LoadAsync( - ApplyFeeCommand cmd, - IAccountQuery query, TimeProvider timeProvider) + public static async Task LoadAsync(ApplyFeeCommand cmd, IAccountQuery query, TimeProvider timeProvider) { var source = await query.FindByIdAsync(cmd.AccountId); var dest = await query.FindByIdAsync(99999); @@ -25,13 +23,8 @@ public static class ApplyFeeHandler return new FeeContext(source, dest, timeProvider.GetUtcNow()); } - public static ( - IStorageAction CustomerWrite, - IStorageAction RevenueWrite, - IStorageAction JournalWrite, - FeeAppliedEvent Event - ) - Handle( + public static ( IStorageAction CustomerWrite, IStorageAction RevenueWrite, + IStorageAction JournalWrite, FeeAppliedEvent Event ) Handle( ApplyFeeCommand cmd, FeeContext context ) @@ -39,25 +32,24 @@ public static class ApplyFeeHandler var updatedSource = context.Source.ApplyPosting(cmd.Amount, EntryType.Debit); var updatedRevenue = context.Destination.ApplyPosting(cmd.Amount, EntryType.Credit); var serviceFee = 0.5m; - + var lines = new List { new(updatedSource.Id, cmd.Amount, EntryType.Debit), new(updatedRevenue.Id, cmd.Amount - serviceFee, EntryType.Credit), new(updatedRevenue.Id, serviceFee, EntryType.Credit) }; - - var journalEntry = BalancedJournal.Create(Guid.NewGuid(), + + var journalEntry = BalancedJournal.Create( + Guid.NewGuid(), $"Service Fee Applied: {cmd.Amount} to Account {cmd.AccountId}", - context.TimeStamp, lines); + context.TimeStamp, + lines + ); var @event = new FeeAppliedEvent(cmd.AccountId, cmd.Amount); - return ( - Storage.Update(updatedSource), - Storage.Update(updatedRevenue), - Storage.Insert(journalEntry.Entry), - @event - ); + return (Storage.Update(updatedSource), Storage.Update(updatedRevenue), Storage.Insert(journalEntry.Entry), + @event); } } \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/FinancialEventHandler.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/FinancialEventHandler.cs index 01c7864..c68ebcb 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/FinancialEventHandler.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/FinancialEventHandler.cs @@ -5,20 +5,14 @@ namespace FinancialApi.Application.Handlers; public static class FinancialEventHandler { - public static void Handle( - FeeAppliedEvent @event, - IEventTracker store, - ILogger logger) + public static void Handle(FeeAppliedEvent @event, IEventTracker store, ILogger logger) { var message = $"FeeApplied: ${@event.Amount} moved from Account {@event.AccountId} to Account 99999."; logger.LogInformation("BACKGROUND EVENT FIRED: {Message}", message); store.Add(message); } - public static void Handle( - FundsTransferredEvent @event, - IEventTracker store, - ILogger logger) + public static void Handle(FundsTransferredEvent @event, IEventTracker store, ILogger logger) { var message = $"FundsTransferred: ${@event.Amount} moved from Account {@event.SourceAccountId} to Account {@event.DestinationAccountId}."; @@ -26,10 +20,7 @@ public static class FinancialEventHandler store.Add(message); } - public static void Handle( - JournalReversedEvent @event, - IEventTracker store, - ILogger logger) + public static void Handle(JournalReversedEvent @event, IEventTracker store, ILogger logger) { var message = $"JournalReversed: Original Entry {@event.OriginalJournalId} was reversed by Entry {@event.ReversalJournalId}."; diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ReverseJournalHandler.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ReverseJournalHandler.cs index f481326..860e85e 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ReverseJournalHandler.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ReverseJournalHandler.cs @@ -11,38 +11,39 @@ namespace FinancialApi.Application.Handlers; public static class ReverseJournalHandler { - public static async Task LoadAsync(ReverseJournalCommand cmd, IReversalQuery query, - TimeProvider timeProvider) + public static async Task LoadAsync( + ReverseJournalCommand cmd, + IReversalQuery query, + TimeProvider timeProvider + ) { var data = await query.GetReversalDataAsync(cmd.OriginalJournalId); return data ?? throw new InvalidOperationException($"Journal Entry {cmd.OriginalJournalId} does not exist"); } - public static ( - IStorageAction JournalWrite, - UnitOfWork AccountWrites, - JournalReversedEvent Event) - Handle(ReverseJournalCommand cmd, JournalReversalContext context) + public static ( IStorageAction JournalWrite, UnitOfWork AccountWrites, JournalReversedEvent + Event) Handle(ReverseJournalCommand cmd, JournalReversalContext context) { - var reversalJournal = - BalancedJournal.CreateReversal(context.OriginalJournalEntry, $"Reversal: {cmd.Reason}", context.TimeStamp); + var reversalJournal = BalancedJournal.CreateReversal( + context.OriginalJournalEntry, + $"Reversal: {cmd.Reason}", + context.TimeStamp + ); var accountState = context.AffectedAccounts.ToDictionary(a => a.Id); reversalJournal.Entry.Lines.ForEach(l => - { - var currentAccount = accountState[l.AccountId]; - accountState[l.AccountId] = currentAccount.ApplyPosting(l.Amount, l.Type); - }); + { + var currentAccount = accountState[l.AccountId]; + accountState[l.AccountId] = currentAccount.ApplyPosting(l.Amount, l.Type); + } + ); var accountUow = new UnitOfWork(); foreach (var account in accountState.Values) { accountUow.Update(account); } - return ( - Storage.Insert(reversalJournal.Entry), - accountUow, - new JournalReversedEvent(cmd.OriginalJournalId, reversalJournal.Entry.Id) - ); + return (Storage.Insert(reversalJournal.Entry), accountUow, + new JournalReversedEvent(cmd.OriginalJournalId, reversalJournal.Entry.Id)); } } \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/TransferFundsHandler.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/TransferFundsHandler.cs index 8f6a27e..33ee246 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/TransferFundsHandler.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/TransferFundsHandler.cs @@ -11,8 +11,11 @@ namespace FinancialApi.Application.Handlers; public static class TransferFundsHandler { - public static async Task LoadAsync(TransferFundsCommand cmd, IAccountQuery query, - TimeProvider timeProvider) + public static async Task LoadAsync( + TransferFundsCommand cmd, + IAccountQuery query, + TimeProvider timeProvider + ) { var source = await query.FindByIdAsync(cmd.SourceAccountId); var dest = await query.FindByIdAsync(cmd.DestinationAccountId); @@ -22,12 +25,8 @@ public static class TransferFundsHandler return new TransferContext(source, dest, timeProvider.GetUtcNow()); } - public static ( - IStorageAction SourceWrite, - IStorageAction DestWrite, - IStorageAction JournalWrite, - FundsTransferredEvent Event - ) Handle(TransferFundsCommand cmd, TransferContext context) + public static ( IStorageAction SourceWrite, IStorageAction DestWrite, IStorageAction + JournalWrite, FundsTransferredEvent Event ) Handle(TransferFundsCommand cmd, TransferContext context) { var updatedSource = context.Source.Debit(cmd.Amount); var updatedDest = context.Destination.Credit(cmd.Amount); @@ -38,18 +37,15 @@ public static class TransferFundsHandler new(context.Destination.Id, cmd.Amount, EntryType.Credit) }; - var journalEntry = - BalancedJournal.Create(Guid.NewGuid(), - $"Transfer: {cmd.Amount} from {cmd.SourceAccountId} to {cmd.DestinationAccountId}", - context.TimeStamp, lines); + var journalEntry = BalancedJournal.Create( + Guid.NewGuid(), + $"Transfer: {cmd.Amount} from {cmd.SourceAccountId} to {cmd.DestinationAccountId}", + context.TimeStamp, + lines + ); var @event = new FundsTransferredEvent(cmd.SourceAccountId, cmd.DestinationAccountId, cmd.Amount); - return ( - Storage.Update(updatedSource), - Storage.Update(updatedDest), - Storage.Insert(journalEntry.Entry), - @event - ); + return (Storage.Update(updatedSource), Storage.Update(updatedDest), Storage.Insert(journalEntry.Entry), @event); } } \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Models/JournalReversalContext.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Models/JournalReversalContext.cs index df79457..886dfd2 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application/Models/JournalReversalContext.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Models/JournalReversalContext.cs @@ -6,4 +6,5 @@ namespace FinancialApi.Application.Models; public record JournalReversalContext( JournalEntry OriginalJournalEntry, IReadOnlyList AffectedAccounts, - DateTimeOffset TimeStamp); \ No newline at end of file + DateTimeOffset TimeStamp +); \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Domain/Aggregates/BalancedJournal.cs b/wolverine/a-frame-architecture/FinancialApi.Domain/Aggregates/BalancedJournal.cs index 19b643e..944e5aa 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Domain/Aggregates/BalancedJournal.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Domain/Aggregates/BalancedJournal.cs @@ -10,8 +10,10 @@ public record BalancedJournal public static BalancedJournal Create(Guid id, string description, DateTimeOffset createdAt, List lines) { - var debits = lines.Where(l => l.Type == EntryType.Debit).Sum(l => l.Amount); - var credits = lines.Where(l => l.Type == EntryType.Credit).Sum(l => l.Amount); + var debits = lines.Where(l => l.Type == EntryType.Debit) + .Sum(l => l.Amount); + var credits = lines.Where(l => l.Type == EntryType.Credit) + .Sum(l => l.Amount); if (debits != credits) { @@ -26,7 +28,8 @@ public record BalancedJournal public static BalancedJournal CreateReversal(JournalEntry original, string reason, DateTimeOffset now) { var reversedLines = original.Lines.Select(l => - l with { Type = l.Type == EntryType.Debit ? EntryType.Credit : EntryType.Debit }) + l with { Type = l.Type == EntryType.Debit ? EntryType.Credit : EntryType.Debit } + ) .ToList(); return Create(Guid.NewGuid(), reason, now, reversedLines); diff --git a/wolverine/a-frame-architecture/FinancialApi.Domain/Entities/JournalEntry.cs b/wolverine/a-frame-architecture/FinancialApi.Domain/Entities/JournalEntry.cs index 85523af..b32bf86 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Domain/Entities/JournalEntry.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Domain/Entities/JournalEntry.cs @@ -7,9 +7,7 @@ public record JournalEntry public DateTimeOffset CreatedAt { get; init; } public List Lines { get; init; } = []; - internal JournalEntry() - { - } + internal JournalEntry() { } internal JournalEntry(Guid id, string description, DateTimeOffset createdAt, List lines) { diff --git a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/AccountDbContext.cs b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/AccountDbContext.cs index 1ef3c3c..06cfb42 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/AccountDbContext.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/AccountDbContext.cs @@ -14,22 +14,30 @@ public class AccountDbContext(DbContextOptions options) : DbCo modelBuilder.Entity() .HasKey(x => x.Id); - modelBuilder.Entity().HasData( - new Account(1, 10000000.00m, "Credit"), - new Account(2, 50000000.00m, "Liability"), - new Account(99999, 0.00m, "Revenue") - ); + modelBuilder.Entity() + .HasData( + new Account(1, 10000000.00m, "Credit"), + new Account(2, 50000000.00m, "Liability"), + new Account(99999, 0.00m, "Revenue") + ); modelBuilder.Entity(builder => - { - builder.HasKey(x => x.Id); - builder.OwnsMany(x => x.Lines, line => { - line.WithOwner().HasForeignKey("JournalEntryId"); - line.Property("Id").ValueGeneratedOnAdd(); - line.HasKey("Id"); - line.Property(x => x.Type).HasConversion(); - }); - }); + builder.HasKey(x => x.Id); + builder.OwnsMany( + x => x.Lines, + line => + { + line.WithOwner() + .HasForeignKey("JournalEntryId"); + line.Property("Id") + .ValueGeneratedOnAdd(); + line.HasKey("Id"); + line.Property(x => x.Type) + .HasConversion(); + } + ); + } + ); } } \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/ReversalQuery.cs b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/ReversalQuery.cs index 6a12db8..3c72954 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/ReversalQuery.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/ReversalQuery.cs @@ -10,8 +10,7 @@ public class ReversalQuery(AccountDbContext db, TimeProvider timeProvider) : IRe { public async Task GetReversalDataAsync(Guid journalEntryId) { - var entry = await db.JournalEntries - .Include(x => x.Lines) + var entry = await db.JournalEntries.Include(x => x.Lines) .AsNoTracking() .FirstOrDefaultAsync(x => x.Id == journalEntryId); @@ -20,10 +19,11 @@ public class ReversalQuery(AccountDbContext db, TimeProvider timeProvider) : IRe return null; } - var accountIds = entry.Lines.Select(l => l.AccountId).Distinct().ToList(); + var accountIds = entry.Lines.Select(l => l.AccountId) + .Distinct() + .ToList(); - var accounts = await db.Accounts - .AsNoTracking() + var accounts = await db.Accounts.AsNoTracking() .Where(a => accountIds.Contains(a.Id)) .ToListAsync(); diff --git a/wolverine/a-frame-architecture/FinancialApi.ServiceDefaults/Extensions.cs b/wolverine/a-frame-architecture/FinancialApi.ServiceDefaults/Extensions.cs index 0257ff4..7f05ac7 100644 --- a/wolverine/a-frame-architecture/FinancialApi.ServiceDefaults/Extensions.cs +++ b/wolverine/a-frame-architecture/FinancialApi.ServiceDefaults/Extensions.cs @@ -27,13 +27,14 @@ public static class Extensions builder.Services.AddServiceDiscovery(); builder.Services.ConfigureHttpClientDefaults(http => - { - // Turn on resilience by default - http.AddStandardResilienceHandler(); + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); - // Turn on service discovery by default - http.AddServiceDiscovery(); - }); + // Turn on service discovery by default + http.AddServiceDiscovery(); + } + ); // Uncomment the following to restrict the allowed schemes for service discovery. // builder.Services.Configure(options => @@ -48,31 +49,34 @@ public static class Extensions where TBuilder : IHostApplicationBuilder { builder.Logging.AddOpenTelemetry(logging => - { - logging.IncludeFormattedMessage = true; - logging.IncludeScopes = true; - }); + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + } + ); builder.Services.AddOpenTelemetry() .WithMetrics(metrics => - { - metrics.AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddRuntimeInstrumentation(); - }) + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + } + ) .WithTracing(tracing => - { - tracing.AddSource(builder.Environment.ApplicationName) - .AddAspNetCoreInstrumentation(tracing => - // Exclude health check requests from tracing - tracing.Filter = context => - !context.Request.Path.StartsWithSegments(HealthEndpointPath) - && !context.Request.Path.StartsWithSegments(AlivenessEndpointPath) - ) - // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) - //.AddGrpcClientInstrumentation() - .AddHttpClientInstrumentation(); - }); + { + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation(tracing => + // Exclude health check requests from tracing + tracing.Filter = context => + !context.Request.Path.StartsWithSegments(HealthEndpointPath) && + !context.Request.Path.StartsWithSegments(AlivenessEndpointPath) + ) + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + } + ); builder.AddOpenTelemetryExporters(); @@ -86,7 +90,8 @@ public static class Extensions if (useOtlpExporter) { - builder.Services.AddOpenTelemetry().UseOtlpExporter(); + builder.Services.AddOpenTelemetry() + .UseOtlpExporter(); } // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) @@ -119,10 +124,10 @@ public static class Extensions app.MapHealthChecks(HealthEndpointPath); // Only health checks tagged with the "live" tag must pass for app to be considered alive - app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions - { - Predicate = r => r.Tags.Contains("live") - }); + app.MapHealthChecks( + AlivenessEndpointPath, + new HealthCheckOptions { Predicate = r => r.Tags.Contains("live") } + ); } return app; diff --git a/wolverine/a-frame-architecture/FinancialApi/DomainExceptionHandler.cs b/wolverine/a-frame-architecture/FinancialApi/DomainExceptionHandler.cs index c7bdd97..181d59c 100644 --- a/wolverine/a-frame-architecture/FinancialApi/DomainExceptionHandler.cs +++ b/wolverine/a-frame-architecture/FinancialApi/DomainExceptionHandler.cs @@ -6,9 +6,10 @@ namespace FinancialApi; public class DomainExceptionHandler : IExceptionHandler { public async ValueTask TryHandleAsync( - HttpContext httpContext, - Exception exception, - CancellationToken cancellationToken) + HttpContext httpContext, + Exception exception, + CancellationToken cancellationToken + ) { if (exception is not (InvalidOperationException or ArgumentException)) { @@ -25,8 +26,7 @@ public class DomainExceptionHandler : IExceptionHandler httpContext.Response.StatusCode = problemDetails.Status.Value; await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken); - - return true; + return true; } } \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi/Program.cs b/wolverine/a-frame-architecture/FinancialApi/Program.cs index bdb87ac..5b3193c 100644 --- a/wolverine/a-frame-architecture/FinancialApi/Program.cs +++ b/wolverine/a-frame-architecture/FinancialApi/Program.cs @@ -19,8 +19,7 @@ builder.Services.AddProblemDetails(); var connectionString = "Data Source=demo.db"; -builder.Services.AddDbContext(options => - options.UseSqlite(connectionString)); +builder.Services.AddDbContext(options => options.UseSqlite(connectionString)); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -33,13 +32,14 @@ builder.Services.AddOpenApi(); builder.AddServiceDefaults(); builder.Host.UseWolverine(opts => -{ - opts.Discovery.IncludeAssembly(typeof(DemoEventStore).Assembly); - opts.Discovery.IncludeAssembly(typeof(ApplyFeeHandler).Assembly); - opts.PersistMessagesWithSqlite(connectionString); - opts.UseEntityFrameworkCoreTransactions(); - opts.Policies.AutoApplyTransactions(); -}); + { + opts.Discovery.IncludeAssembly(typeof(DemoEventStore).Assembly); + opts.Discovery.IncludeAssembly(typeof(ApplyFeeHandler).Assembly); + opts.PersistMessagesWithSqlite(connectionString); + opts.UseEntityFrameworkCoreTransactions(); + opts.Policies.AutoApplyTransactions(); + } +); builder.Host.UseResourceSetupOnStartup(); var app = builder.Build(); @@ -60,32 +60,44 @@ if (app.Environment.IsDevelopment()) app.UseHttpsRedirection(); -app.MapPost("/accounts/apply-fee", async (ApplyFeeCommand cmd, IMessageBus bus) => -{ - await bus.InvokeAsync(cmd); - return Results.Accepted(); -}); - -app.MapPost("/accounts/transfer", async (TransferFundsCommand cmd, IMessageBus bus) => -{ - await bus.InvokeAsync(cmd); - return Results.Accepted(); -}); - -app.MapPost("/journal/reverse", async (ReverseJournalCommand cmd, IMessageBus bus) => -{ - await bus.InvokeAsync(cmd); - return Results.Accepted(); -}); - -app.MapGet("/events", (IEventTracker tracker) => -{ - if (tracker is DemoEventStore store) +app.MapPost( + "/accounts/apply-fee", + async (ApplyFeeCommand cmd, IMessageBus bus) => { - return Results.Ok(store.CapturedEvents); + await bus.InvokeAsync(cmd); + return Results.Accepted(); } +); - return Results.NotFound(); -}); +app.MapPost( + "/accounts/transfer", + async (TransferFundsCommand cmd, IMessageBus bus) => + { + await bus.InvokeAsync(cmd); + return Results.Accepted(); + } +); + +app.MapPost( + "/journal/reverse", + async (ReverseJournalCommand cmd, IMessageBus bus) => + { + await bus.InvokeAsync(cmd); + return Results.Accepted(); + } +); + +app.MapGet( + "/events", + (IEventTracker tracker) => + { + if (tracker is DemoEventStore store) + { + return Results.Ok(store.CapturedEvents); + } + + return Results.NotFound(); + } +); app.Run(); \ No newline at end of file