diff --git a/wolverine/a-frame-architecture/FinancialApi.Application.UnitTests/ApplyFeeHandlerTests.cs b/wolverine/a-frame-architecture/FinancialApi.Application.UnitTests/ApplyFeeHandlerTests.cs index 7292a17..3362803 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application.UnitTests/ApplyFeeHandlerTests.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application.UnitTests/ApplyFeeHandlerTests.cs @@ -1,4 +1,7 @@ -using FinancialApi.Domain; +using System.Security.Cryptography; +using FinancialApi.Application.Commands; +using FinancialApi.Application.Handlers; +using FinancialApi.Domain; using FluentAssertions; using FluentAssertions.Execution; @@ -12,19 +15,18 @@ public class ApplyFeeHandlerTests // Arrange var command = new ApplyFeeCommand(1, 100); var account = new Account(1, 100, "Debit"); - var destAccount = new Account(99999, 100, "Credit"); - var pair = new FeePair(account, destAccount, TimeProvider.System.GetUtcNow()); - + var revenueAccount = new Account(99999, 0, "Credit"); + var pair = new FeePair(account, revenueAccount, TimeProvider.System.GetUtcNow()); + // Act var intents = ApplyFeeHandler.Handle(command, pair); // Assert using var _ = new AssertionScope(); - intents.CustomerWrite.Entity.Balance.Should().Be(0); - intents.RevenueWrite.Entity.Balance.Should().Be(200); - intents.RevenueWrite.Entity.Balance.Should().Be(200); - intents.JournalWrite.Entity.Lines.Count.Should().Be(2); + 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(100); + intents.Event.Amount.Should().Be(command.Amount); } } \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Commands/ApplyFeeCommand.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Commands/ApplyFeeCommand.cs new file mode 100644 index 0000000..92fd480 --- /dev/null +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Commands/ApplyFeeCommand.cs @@ -0,0 +1,3 @@ +namespace FinancialApi.Application.Commands; + +public record ApplyFeeCommand(int AccountId, decimal Amount); \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Commands/ReverseJournalCommand.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Commands/ReverseJournalCommand.cs new file mode 100644 index 0000000..66e954b --- /dev/null +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Commands/ReverseJournalCommand.cs @@ -0,0 +1,3 @@ +namespace FinancialApi.Application.Commands; + +public record ReverseJournalCommand(Guid OriginalJournalId, string Reason); \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Commands/TransferFundsCommand.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Commands/TransferFundsCommand.cs new file mode 100644 index 0000000..166a158 --- /dev/null +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Commands/TransferFundsCommand.cs @@ -0,0 +1,3 @@ +namespace FinancialApi.Application.Commands; + +public abstract record TransferFundsCommand(int SourceAccountId, int DestinationAccountId, decimal Amount); \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Events/FeeAppliedEvent.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Events/FeeAppliedEvent.cs new file mode 100644 index 0000000..d1619b7 --- /dev/null +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Events/FeeAppliedEvent.cs @@ -0,0 +1,3 @@ +namespace FinancialApi.Application.Events; + +public record FeeAppliedEvent(int AccountId, decimal Amount); \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Events/FundsTransferredEvent.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Events/FundsTransferredEvent.cs new file mode 100644 index 0000000..7913ad2 --- /dev/null +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Events/FundsTransferredEvent.cs @@ -0,0 +1,3 @@ +namespace FinancialApi.Application.Events; + +public record FundsTransferredEvent(int SourceAccountId, int DestinationAccountId, decimal Amount); \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/IEventTracker.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Events/IEventTracker.cs similarity index 61% rename from wolverine/a-frame-architecture/FinancialApi.Application/IEventTracker.cs rename to wolverine/a-frame-architecture/FinancialApi.Application/Events/IEventTracker.cs index 14bdab6..3ae0a84 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application/IEventTracker.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Events/IEventTracker.cs @@ -1,4 +1,4 @@ -namespace FinancialApi.Application; +namespace FinancialApi.Application.Events; public interface IEventTracker { diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Events/JournalReversedEvent.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Events/JournalReversedEvent.cs new file mode 100644 index 0000000..160433a --- /dev/null +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Events/JournalReversedEvent.cs @@ -0,0 +1,3 @@ +namespace FinancialApi.Application.Events; + +public record JournalReversedEvent(Guid OriginalJournalId, Guid ReversalJournalId); \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/FeePair.cs b/wolverine/a-frame-architecture/FinancialApi.Application/FeePair.cs new file mode 100644 index 0000000..a183e90 --- /dev/null +++ b/wolverine/a-frame-architecture/FinancialApi.Application/FeePair.cs @@ -0,0 +1,5 @@ +using FinancialApi.Domain; + +namespace FinancialApi.Application; + +public record FeePair(Account Source, Account Destination, DateTimeOffset TimeStamp); \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/ApplyFeeHandler.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ApplyFeeHandler.cs similarity index 80% rename from wolverine/a-frame-architecture/FinancialApi.Application/ApplyFeeHandler.cs rename to wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ApplyFeeHandler.cs index d9d5e04..96ebf8e 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application/ApplyFeeHandler.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ApplyFeeHandler.cs @@ -1,16 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; +using FinancialApi.Application.Commands; +using FinancialApi.Application.Events; +using FinancialApi.Application.Queries; using FinancialApi.Domain; using Wolverine.Persistence; -namespace FinancialApi.Application; - -public record ApplyFeeCommand(int AccountId, decimal Amount); - -public record FeeAppliedEvent(int AccountId, decimal Amount); - -public record FeePair(Account Source, Account Destination, DateTimeOffset TimeStamp); +namespace FinancialApi.Application.Handlers; public static class ApplyFeeHandler { @@ -41,11 +35,15 @@ public static class ApplyFeeHandler { var updatedSource = pair.Source.ApplyPosting(cmd.Amount, EntryType.Debit); var updatedRevenue = pair.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, EntryType.Credit) + new(updatedRevenue.Id, cmd.Amount - serviceFee, EntryType.Credit), + new(updatedRevenue.Id, serviceFee, EntryType.Credit) }; + var journalEntry = BalancedJournal.Create(Guid.NewGuid(), $"Service Fee Applied: {cmd.Amount} to Account {cmd.AccountId}", pair.TimeStamp, lines); diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/FinancialEventHandler.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/FinancialEventHandler.cs similarity index 93% rename from wolverine/a-frame-architecture/FinancialApi.Application/FinancialEventHandler.cs rename to wolverine/a-frame-architecture/FinancialApi.Application/Handlers/FinancialEventHandler.cs index 4a6fdec..01c7864 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application/FinancialEventHandler.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/FinancialEventHandler.cs @@ -1,6 +1,7 @@ +using FinancialApi.Application.Events; using Microsoft.Extensions.Logging; -namespace FinancialApi.Application; +namespace FinancialApi.Application.Handlers; public static class FinancialEventHandler { diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/ReverseJournalHandler.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ReverseJournalHandler.cs similarity index 75% rename from wolverine/a-frame-architecture/FinancialApi.Application/ReverseJournalHandler.cs rename to wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ReverseJournalHandler.cs index d4158dd..7f2e1b3 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application/ReverseJournalHandler.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/ReverseJournalHandler.cs @@ -1,26 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using FinancialApi.Application.Commands; +using FinancialApi.Application.Events; +using FinancialApi.Application.Queries; using FinancialApi.Domain; using Wolverine.Persistence; -namespace FinancialApi.Application; - -public record ReverseJournalCommand(Guid OriginalJournalId, string Reason); - -public record JournalReversedEvent(Guid OriginalJournalId, Guid ReversalJournalId); +namespace FinancialApi.Application.Handlers; public record ReversalData( JournalEntry OriginalJournalEntry, IReadOnlyList AffectedAccounts, DateTimeOffset TimeStamp); -public interface IReversalQuery -{ - Task GetReversalDataAsync(Guid journalEntryId); -} - public static class ReverseJournalHandler { public static async Task LoadAsync(ReverseJournalCommand cmd, IReversalQuery query, @@ -33,7 +23,8 @@ public static class ReverseJournalHandler public static ( IStorageAction JournalWrite, UnitOfWork AccountWrites, - JournalReversedEvent Event) Handle(ReverseJournalCommand cmd, ReversalData data) + JournalReversedEvent Event) + Handle(ReverseJournalCommand cmd, ReversalData data) { var reversalJournal = BalancedJournal.CreateReversal(data.OriginalJournalEntry, $"Reversal: {cmd.Reason}", data.TimeStamp); diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/TransferFundsHandler.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/TransferFundsHandler.cs similarity index 76% rename from wolverine/a-frame-architecture/FinancialApi.Application/TransferFundsHandler.cs rename to wolverine/a-frame-architecture/FinancialApi.Application/Handlers/TransferFundsHandler.cs index a7c902b..209ddf2 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Application/TransferFundsHandler.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Handlers/TransferFundsHandler.cs @@ -1,21 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; +using FinancialApi.Application.Commands; +using FinancialApi.Application.Events; +using FinancialApi.Application.Queries; using FinancialApi.Domain; using Wolverine.Persistence; -namespace FinancialApi.Application; - -public interface IAccountQuery -{ - Task FindByIdAsync(int id); -} - -public record TransferFundsCommand(int SourceAccountId, int DestinationAccountId, decimal Amount); - -public record FundsTransferredEvent(int SourceAccountId, int DestinationAccountId, decimal Amount); - -public record TransferPair(Account Source, Account Destination, DateTimeOffset TimeStamp); +namespace FinancialApi.Application.Handlers; public static class TransferFundsHandler { diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Queries/IAccountQuery.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Queries/IAccountQuery.cs new file mode 100644 index 0000000..887e612 --- /dev/null +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Queries/IAccountQuery.cs @@ -0,0 +1,8 @@ +using FinancialApi.Domain; + +namespace FinancialApi.Application.Queries; + +public interface IAccountQuery +{ + Task FindByIdAsync(int id); +} \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Queries/IReversalQuery.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Queries/IReversalQuery.cs new file mode 100644 index 0000000..3925eb2 --- /dev/null +++ b/wolverine/a-frame-architecture/FinancialApi.Application/Queries/IReversalQuery.cs @@ -0,0 +1,8 @@ +using FinancialApi.Application.Handlers; + +namespace FinancialApi.Application.Queries; + +public interface IReversalQuery +{ + Task GetReversalDataAsync(Guid journalEntryId); +} \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/TransferPair.cs b/wolverine/a-frame-architecture/FinancialApi.Application/TransferPair.cs new file mode 100644 index 0000000..4f19502 --- /dev/null +++ b/wolverine/a-frame-architecture/FinancialApi.Application/TransferPair.cs @@ -0,0 +1,5 @@ +using FinancialApi.Domain; + +namespace FinancialApi.Application; + +public record TransferPair(Account Source, Account Destination, DateTimeOffset TimeStamp); \ No newline at end of file diff --git a/wolverine/a-frame-architecture/FinancialApi.Domain/Account.cs b/wolverine/a-frame-architecture/FinancialApi.Domain/Account.cs index 478e6f3..0d4d592 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Domain/Account.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Domain/Account.cs @@ -1,6 +1,4 @@ -using System; - -namespace FinancialApi.Domain; +namespace FinancialApi.Domain; public record Account(int Id, decimal Balance, string AccountType) { diff --git a/wolverine/a-frame-architecture/FinancialApi.Domain/BalancedJournal.cs b/wolverine/a-frame-architecture/FinancialApi.Domain/BalancedJournal.cs index e10bde2..07c9435 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Domain/BalancedJournal.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Domain/BalancedJournal.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; - namespace FinancialApi.Domain; public record BalancedJournal diff --git a/wolverine/a-frame-architecture/FinancialApi.Domain/JournalEntry.cs b/wolverine/a-frame-architecture/FinancialApi.Domain/JournalEntry.cs index 7f39007..649fe2f 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Domain/JournalEntry.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Domain/JournalEntry.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; - namespace FinancialApi.Domain; public record JournalEntry diff --git a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/AccountQuery.cs b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/AccountQuery.cs index 34d9827..be5aa70 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/AccountQuery.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/AccountQuery.cs @@ -1,5 +1,4 @@ -using System.Threading.Tasks; -using FinancialApi.Application; +using FinancialApi.Application.Queries; using FinancialApi.Domain; using Microsoft.EntityFrameworkCore; diff --git a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/ReversalQuery.cs b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/ReversalQuery.cs index e8dad99..44e82ae 100644 --- a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/ReversalQuery.cs +++ b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/ReversalQuery.cs @@ -1,7 +1,5 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using FinancialApi.Application; +using FinancialApi.Application.Handlers; +using FinancialApi.Application.Queries; using Microsoft.EntityFrameworkCore; namespace FinancialApi.Infrastructure; diff --git a/wolverine/a-frame-architecture/FinancialApi/DemoEventStore.cs b/wolverine/a-frame-architecture/FinancialApi/DemoEventStore.cs index a7d8b11..7a174f8 100644 --- a/wolverine/a-frame-architecture/FinancialApi/DemoEventStore.cs +++ b/wolverine/a-frame-architecture/FinancialApi/DemoEventStore.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using FinancialApi.Application; +using FinancialApi.Application.Events; namespace FinancialApi; diff --git a/wolverine/a-frame-architecture/FinancialApi/DomainExceptionHandler.cs b/wolverine/a-frame-architecture/FinancialApi/DomainExceptionHandler.cs index 857d55e..c7bdd97 100644 --- a/wolverine/a-frame-architecture/FinancialApi/DomainExceptionHandler.cs +++ b/wolverine/a-frame-architecture/FinancialApi/DomainExceptionHandler.cs @@ -1,8 +1,4 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.AspNetCore.Diagnostics; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace FinancialApi; diff --git a/wolverine/a-frame-architecture/FinancialApi/FinancialApi.http b/wolverine/a-frame-architecture/FinancialApi/FinancialApi.http index 840a3a5..85ee0e2 100644 --- a/wolverine/a-frame-architecture/FinancialApi/FinancialApi.http +++ b/wolverine/a-frame-architecture/FinancialApi/FinancialApi.http @@ -1,8 +1,5 @@ @FinancialApi_HostAddress = https://localhost:7010 -GET {{FinancialApi_HostAddress}}/weatherforecast/ -Accept: application/json - ### Apply Fee to an Account POST {{FinancialApi_HostAddress}}/accounts/apply-fee @@ -11,7 +8,7 @@ Content-Type: application/json { "accountId": 1, - "amount": 500.0 + "amount": 50.0 } ### Transfer Between Accounts @@ -31,7 +28,7 @@ POST {{FinancialApi_HostAddress}}/journal/reverse Content-Type: application/json { - "originalJournalId": "1443B0FC-F679-4445-A910-CC333F0925B7", + "originalJournalId": "FD2BFD8E-7C85-4CAD-A12B-CCBCD7C12B4A", "reason": "Incorrect Account Fee" } diff --git a/wolverine/a-frame-architecture/FinancialApi/Program.cs b/wolverine/a-frame-architecture/FinancialApi/Program.cs index 1d52173..f71b462 100644 --- a/wolverine/a-frame-architecture/FinancialApi/Program.cs +++ b/wolverine/a-frame-architecture/FinancialApi/Program.cs @@ -1,13 +1,12 @@ using FinancialApi; -using FinancialApi.Application; +using FinancialApi.Application.Commands; +using FinancialApi.Application.Events; +using FinancialApi.Application.Handlers; +using FinancialApi.Application.Queries; using FinancialApi.Infrastructure; using FinancialApi.ServiceDefaults; using JasperFx.Resources; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Wolverine; using Wolverine.EntityFrameworkCore; using Wolverine.Sqlite; @@ -85,6 +84,7 @@ app.MapGet("/events", (IEventTracker tracker) => { return Results.Ok(store.CapturedEvents); } + return Results.NotFound(); });