diff --git a/wolverine/a-frame-architecture/FinacialApi/FinancialApi.http b/wolverine/a-frame-architecture/FinacialApi/FinancialApi.http
deleted file mode 100644
index 59806ef..0000000
--- a/wolverine/a-frame-architecture/FinacialApi/FinancialApi.http
+++ /dev/null
@@ -1,6 +0,0 @@
-@FinancialApi_HostAddress = https://localhost:7010
-
-GET {{FinancialApi_HostAddress}}/weatherforecast/
-Accept: application/json
-
-###
diff --git a/wolverine/a-frame-architecture/FinacialApi/Program.cs b/wolverine/a-frame-architecture/FinacialApi/Program.cs
deleted file mode 100644
index 124a24e..0000000
--- a/wolverine/a-frame-architecture/FinacialApi/Program.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using FinancialApi.ServiceDefaults;
-
-var builder = WebApplication.CreateBuilder(args);
-
-// Add services to the container.
-// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
-builder.Services.AddOpenApi();
-
-builder.AddServiceDefaults();
-
-var app = builder.Build();
-
-// Configure the HTTP request pipeline.
-if (app.Environment.IsDevelopment())
-{
- app.MapOpenApi();
-}
-
-app.UseHttpsRedirection();
-
-var summaries = new[]
-{
- "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
-};
-
-app.MapGet("/weatherforecast", () =>
- {
- var forecast = Enumerable.Range(1, 5).Select(index =>
- new WeatherForecast
- (
- DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
- Random.Shared.Next(-20, 55),
- summaries[Random.Shared.Next(summaries.Length)]
- ))
- .ToArray();
- return forecast;
- })
- .WithName("GetWeatherForecast");
-
-app.Run();
-
-record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
-{
- public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
-}
\ No newline at end of file
diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/Class1.cs b/wolverine/a-frame-architecture/FinancialApi.Application/Class1.cs
new file mode 100644
index 0000000..2687d92
--- /dev/null
+++ b/wolverine/a-frame-architecture/FinancialApi.Application/Class1.cs
@@ -0,0 +1,16 @@
+namespace FinancialApi.Application;
+
+public record Account(int Id, decimal Balance)
+{
+ public Account ApplyFee(decimal amount)
+ {
+ if (amount <= 0)
+ throw new ArgumentException("Fee must be positive.");
+
+ if (Balance - amount < 0)
+ throw new InvalidOperationException("Insufficient funds.");
+
+ // Returns a brand new record with the updated balance
+ return this with { Balance = Balance - amount };
+ }
+}
\ No newline at end of file
diff --git a/wolverine/a-frame-architecture/FinancialApi.Application/FinancialApi.Application.csproj b/wolverine/a-frame-architecture/FinancialApi.Application/FinancialApi.Application.csproj
new file mode 100644
index 0000000..237d661
--- /dev/null
+++ b/wolverine/a-frame-architecture/FinancialApi.Application/FinancialApi.Application.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/Class1.cs b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/Class1.cs
new file mode 100644
index 0000000..f0a2233
--- /dev/null
+++ b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/Class1.cs
@@ -0,0 +1,24 @@
+using FinancialApi.Application;
+using Microsoft.EntityFrameworkCore;
+
+namespace FinancialApi.Infrastructure;
+
+public class AccountDbContext : DbContext
+{
+ public AccountDbContext(DbContextOptions options) : base(options)
+ {
+ }
+
+ public DbSet Accounts => Set();
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity()
+ .HasKey(x => x.Id);
+
+ modelBuilder.Entity().HasData(
+ new Account(1, 100.00m),
+ new Account(2, 500.00m)
+ );
+ }
+}
\ No newline at end of file
diff --git a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/FinancialApi.Infrastructure.csproj b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/FinancialApi.Infrastructure.csproj
new file mode 100644
index 0000000..5a597d6
--- /dev/null
+++ b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/FinancialApi.Infrastructure.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/Migrations/20260713152041_InitialCreate.Designer.cs b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/Migrations/20260713152041_InitialCreate.Designer.cs
new file mode 100644
index 0000000..b101970
--- /dev/null
+++ b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/Migrations/20260713152041_InitialCreate.Designer.cs
@@ -0,0 +1,50 @@
+//
+using FinancialApi.Infrastructure;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace FinancialApi.Infrastructure.Migrations
+{
+ [DbContext(typeof(AccountDbContext))]
+ [Migration("20260713152041_InitialCreate")]
+ partial class InitialCreate
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "10.0.9");
+
+ modelBuilder.Entity("FinancialApi.Application.Account", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Balance")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("Accounts");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ Balance = 100.00m
+ },
+ new
+ {
+ Id = 2,
+ Balance = 500.00m
+ });
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/Migrations/20260713152041_InitialCreate.cs b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/Migrations/20260713152041_InitialCreate.cs
new file mode 100644
index 0000000..b5cfec2
--- /dev/null
+++ b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/Migrations/20260713152041_InitialCreate.cs
@@ -0,0 +1,45 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
+
+namespace FinancialApi.Infrastructure.Migrations
+{
+ ///
+ public partial class InitialCreate : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "Accounts",
+ columns: table => new
+ {
+ Id = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ Balance = table.Column(type: "TEXT", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Accounts", x => x.Id);
+ });
+
+ migrationBuilder.InsertData(
+ table: "Accounts",
+ columns: new[] { "Id", "Balance" },
+ values: new object[,]
+ {
+ { 1, 100.00m },
+ { 2, 500.00m }
+ });
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "Accounts");
+ }
+ }
+}
diff --git a/wolverine/a-frame-architecture/FinancialApi.Infrastructure/Migrations/AccountDbContextModelSnapshot.cs b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/Migrations/AccountDbContextModelSnapshot.cs
new file mode 100644
index 0000000..1f84a63
--- /dev/null
+++ b/wolverine/a-frame-architecture/FinancialApi.Infrastructure/Migrations/AccountDbContextModelSnapshot.cs
@@ -0,0 +1,47 @@
+//
+using FinancialApi.Infrastructure;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace FinancialApi.Infrastructure.Migrations
+{
+ [DbContext(typeof(AccountDbContext))]
+ partial class AccountDbContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "10.0.9");
+
+ modelBuilder.Entity("FinancialApi.Application.Account", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Balance")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("Accounts");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ Balance = 100.00m
+ },
+ new
+ {
+ Id = 2,
+ Balance = 500.00m
+ });
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/wolverine/a-frame-architecture/FinancialApi/ApplyFeeHandler.cs b/wolverine/a-frame-architecture/FinancialApi/ApplyFeeHandler.cs
new file mode 100644
index 0000000..47adfd2
--- /dev/null
+++ b/wolverine/a-frame-architecture/FinancialApi/ApplyFeeHandler.cs
@@ -0,0 +1,25 @@
+using FinancialApi.Application;
+using FinancialApi.Infrastructure;
+using Microsoft.EntityFrameworkCore;
+
+namespace FinancialApi;
+
+public static class ApplyFeeHandler
+{
+ public static Task LoadAsync(ApplyFeeCommand cmd, AccountDbContext db)
+ => db.Set().AsNoTracking()
+ .FirstOrDefaultAsync(x => x.Id == cmd.AccountId);
+
+ public static (Account UpdatedAccount, FeeAppliedEvent Event) Handle(ApplyFeeCommand cmd, Account account)
+ {
+ var updatedAccount = account.ApplyFee(cmd.Amount);
+
+ var @event = new FeeAppliedEvent(account.Id, cmd.Amount);
+
+ return (updatedAccount, @event);
+ }
+}
+
+public record ApplyFeeCommand(int AccountId, decimal Amount);
+
+public record FeeAppliedEvent(int AccountId, decimal Amount);
\ No newline at end of file
diff --git a/wolverine/a-frame-architecture/FinacialApi/FinacialApi.csproj b/wolverine/a-frame-architecture/FinancialApi/FinancialApi.csproj
similarity index 54%
rename from wolverine/a-frame-architecture/FinacialApi/FinacialApi.csproj
rename to wolverine/a-frame-architecture/FinancialApi/FinancialApi.csproj
index aad38d9..510d782 100644
--- a/wolverine/a-frame-architecture/FinacialApi/FinacialApi.csproj
+++ b/wolverine/a-frame-architecture/FinancialApi/FinancialApi.csproj
@@ -8,9 +8,14 @@
+
+
+
+
+
diff --git a/wolverine/a-frame-architecture/FinancialApi/FinancialApi.http b/wolverine/a-frame-architecture/FinancialApi/FinancialApi.http
new file mode 100644
index 0000000..c4c3e4c
--- /dev/null
+++ b/wolverine/a-frame-architecture/FinancialApi/FinancialApi.http
@@ -0,0 +1,17 @@
+@FinancialApi_HostAddress = https://localhost:7010
+
+GET {{FinancialApi_HostAddress}}/weatherforecast/
+Accept: application/json
+
+###
+
+POST {{FinancialApi_HostAddress}}/accounts/apply-fee
+Accept: application/json
+Content-Type: application/json
+
+{
+ "accountId": 1,
+ "amount": 5.0
+}
+
+###
diff --git a/wolverine/a-frame-architecture/FinancialApi/Program.cs b/wolverine/a-frame-architecture/FinancialApi/Program.cs
new file mode 100644
index 0000000..4a512a0
--- /dev/null
+++ b/wolverine/a-frame-architecture/FinancialApi/Program.cs
@@ -0,0 +1,50 @@
+using FinancialApi;
+using FinancialApi.Infrastructure;
+using FinancialApi.ServiceDefaults;
+using JasperFx.CodeGeneration;
+using Microsoft.EntityFrameworkCore;
+using Wolverine;
+using Wolverine.EntityFrameworkCore;
+using Wolverine.Sqlite;
+
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddDbContext(options =>
+ options.UseSqlite("Data Source=demo.db"));
+
+// Add services to the container.
+// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
+builder.Services.AddOpenApi();
+
+builder.AddServiceDefaults();
+
+builder.Host.UseWolverine(opts =>
+{
+ opts.PersistMessagesWithSqlite("Data Source=demo.db");
+ opts.UseEntityFrameworkCoreTransactions();
+});
+
+var app = builder.Build();
+
+using (var scope = app.Services.CreateScope())
+{
+ var db = scope.ServiceProvider.GetRequiredService();
+ await db.Database.MigrateAsync();
+}
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.MapOpenApi();
+}
+
+app.UseHttpsRedirection();
+
+app.MapPost("/accounts/apply-fee", async (ApplyFeeCommand cmd, IMessageBus bus) =>
+{
+ await bus.InvokeAsync(cmd);
+ return Results.Accepted();
+});
+
+app.Run();
diff --git a/wolverine/a-frame-architecture/FinacialApi/Properties/launchSettings.json b/wolverine/a-frame-architecture/FinancialApi/Properties/launchSettings.json
similarity index 100%
rename from wolverine/a-frame-architecture/FinacialApi/Properties/launchSettings.json
rename to wolverine/a-frame-architecture/FinancialApi/Properties/launchSettings.json
diff --git a/wolverine/a-frame-architecture/FinacialApi/appsettings.Development.json b/wolverine/a-frame-architecture/FinancialApi/appsettings.Development.json
similarity index 100%
rename from wolverine/a-frame-architecture/FinacialApi/appsettings.Development.json
rename to wolverine/a-frame-architecture/FinancialApi/appsettings.Development.json
diff --git a/wolverine/a-frame-architecture/FinacialApi/appsettings.json b/wolverine/a-frame-architecture/FinancialApi/appsettings.json
similarity index 100%
rename from wolverine/a-frame-architecture/FinacialApi/appsettings.json
rename to wolverine/a-frame-architecture/FinancialApi/appsettings.json
diff --git a/wolverine/a-frame-architecture/FinancialApi/demo.db b/wolverine/a-frame-architecture/FinancialApi/demo.db
new file mode 100644
index 0000000..4410bda
Binary files /dev/null and b/wolverine/a-frame-architecture/FinancialApi/demo.db differ
diff --git a/wolverine/a-frame-architecture/FinancialApi/demo.db-shm b/wolverine/a-frame-architecture/FinancialApi/demo.db-shm
new file mode 100644
index 0000000..beb79bc
Binary files /dev/null and b/wolverine/a-frame-architecture/FinancialApi/demo.db-shm differ
diff --git a/wolverine/a-frame-architecture/FinancialApi/demo.db-wal b/wolverine/a-frame-architecture/FinancialApi/demo.db-wal
new file mode 100644
index 0000000..0c41723
Binary files /dev/null and b/wolverine/a-frame-architecture/FinancialApi/demo.db-wal differ
diff --git a/wolverine/a-frame-architecture/a-frame-architecture.sln b/wolverine/a-frame-architecture/a-frame-architecture.sln
index 6b57ec0..ab94823 100644
--- a/wolverine/a-frame-architecture/a-frame-architecture.sln
+++ b/wolverine/a-frame-architecture/a-frame-architecture.sln
@@ -2,7 +2,11 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinancialApi.ServiceDefaults", "FinancialApi.ServiceDefaults\FinancialApi.ServiceDefaults.csproj", "{D9767D77-0B2D-4653-9B9C-A0E6A3850EA3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinacialApi", "FinacialApi\FinacialApi.csproj", "{9EFD8279-869B-485C-BA02-C983E96348A0}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinancialApi", "FinancialApi\FinancialApi.csproj", "{9EFD8279-869B-485C-BA02-C983E96348A0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinancialApi.Application", "FinancialApi.Application\FinancialApi.Application.csproj", "{B7EB1ECE-9F7F-440B-8818-28A127A99915}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinancialApi.Infrastructure", "FinancialApi.Infrastructure\FinancialApi.Infrastructure.csproj", "{6AB36AB5-05BD-41C8-9498-63EBDFAF8114}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -18,5 +22,13 @@ Global
{9EFD8279-869B-485C-BA02-C983E96348A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9EFD8279-869B-485C-BA02-C983E96348A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9EFD8279-869B-485C-BA02-C983E96348A0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B7EB1ECE-9F7F-440B-8818-28A127A99915}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B7EB1ECE-9F7F-440B-8818-28A127A99915}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B7EB1ECE-9F7F-440B-8818-28A127A99915}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B7EB1ECE-9F7F-440B-8818-28A127A99915}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6AB36AB5-05BD-41C8-9498-63EBDFAF8114}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6AB36AB5-05BD-41C8-9498-63EBDFAF8114}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6AB36AB5-05BD-41C8-9498-63EBDFAF8114}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6AB36AB5-05BD-41C8-9498-63EBDFAF8114}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal