diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/.gitignore b/csharp-architectural-patterns/VerticalSliceArchitecture/.gitignore
new file mode 100644
index 0000000000..1998dc0e60
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/.gitignore
@@ -0,0 +1,3 @@
+tickets.db
+tickets.db-shm
+tickets.db-wal
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing.Tests/EventTicketing.Tests.csproj b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing.Tests/EventTicketing.Tests.csproj
new file mode 100644
index 0000000000..9530da9bd1
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing.Tests/EventTicketing.Tests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing.Tests/ReserveTicketsTests.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing.Tests/ReserveTicketsTests.cs
new file mode 100644
index 0000000000..76b88cb16d
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing.Tests/ReserveTicketsTests.cs
@@ -0,0 +1,55 @@
+using EventTicketing.Data;
+using EventTicketing.Features.Reservations;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+
+namespace EventTicketing.Tests;
+
+public sealed class ReserveTicketsTests : IDisposable
+{
+ private readonly SqliteConnection _connection = new("Data Source=:memory:");
+
+ public ReserveTicketsTests() => _connection.Open();
+
+ public void Dispose() => _connection.Dispose();
+
+ private TicketingDbContext CreateDbContext()
+ {
+ var options = new DbContextOptionsBuilder()
+ .UseSqlite(_connection)
+ .Options;
+
+ var dbContext = new TicketingDbContext(options);
+ dbContext.Database.EnsureCreated();
+
+ return dbContext;
+ }
+
+ [Fact]
+ public async Task HandleAsync_WhenTicketsAreAvailable_SavesTheReservation()
+ {
+ var handler = new ReserveTickets.Handler(CreateDbContext());
+
+ var result = await handler.HandleAsync(eventId: 1, quantity: 3, TestContext.Current.CancellationToken);
+
+ Assert.True(result.IsSuccess);
+ Assert.Equal(97, result.Value.TicketsLeft);
+
+ var saved = await CreateDbContext().Events.SingleAsync(e => e.Id == 1, TestContext.Current.CancellationToken);
+ Assert.Equal(3, saved.TicketsSold);
+ }
+
+ [Fact]
+ public async Task HandleAsync_WhenTheEventIsSoldOut_SavesNothing()
+ {
+ var handler = new ReserveTickets.Handler(CreateDbContext());
+
+ var result = await handler.HandleAsync(eventId: 2, quantity: 3, TestContext.Current.CancellationToken);
+
+ Assert.False(result.IsSuccess);
+ Assert.Equal("Event.SoldOut", result.Error!.Code);
+
+ var saved = await CreateDbContext().Events.SingleAsync(e => e.Id == 2, TestContext.Current.CancellationToken);
+ Assert.Equal(0, saved.TicketsSold);
+ }
+}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing.Tests/SliceTests.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing.Tests/SliceTests.cs
new file mode 100644
index 0000000000..9bdd84d73e
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing.Tests/SliceTests.cs
@@ -0,0 +1,35 @@
+using EventTicketing.Features.Reservations;
+using NetArchTest.Rules;
+
+namespace EventTicketing.Tests;
+
+public class SliceTests
+{
+ [Fact]
+ public void Features_DoNotDependOnEachOther()
+ {
+ var assembly = typeof(ReserveTickets).Assembly;
+
+ var features = assembly.GetTypes()
+ .Select(t => t.Namespace)
+ .OfType()
+ .Where(ns => ns.StartsWith("EventTicketing.Features."))
+ .Distinct()
+ .ToArray();
+
+ foreach (var feature in features)
+ {
+ var otherFeatures = features.Where(f => f != feature).ToArray();
+
+ var result = Types.InAssembly(assembly)
+ .That()
+ .ResideInNamespace(feature)
+ .ShouldNot()
+ .HaveDependencyOnAny(otherFeatures)
+ .GetResult();
+
+ Assert.True(result.IsSuccessful,
+ $"{feature} depends on another feature: " + string.Join(", ", result.FailingTypeNames ?? []));
+ }
+ }
+}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing.sln b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing.sln
new file mode 100644
index 0000000000..9eb5cb74d7
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing.sln
@@ -0,0 +1,48 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing", "EventTicketing\EventTicketing.csproj", "{5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.Tests", "EventTicketing.Tests\EventTicketing.Tests.csproj", "{4B09EE23-0672-4FE0-8078-430028A5351E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}.Debug|x64.Build.0 = Debug|Any CPU
+ {5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}.Debug|x86.Build.0 = Debug|Any CPU
+ {5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}.Release|x64.ActiveCfg = Release|Any CPU
+ {5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}.Release|x64.Build.0 = Release|Any CPU
+ {5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}.Release|x86.ActiveCfg = Release|Any CPU
+ {5FAF63F8-89C3-4EEE-A3A0-DCB365CA69B6}.Release|x86.Build.0 = Release|Any CPU
+ {4B09EE23-0672-4FE0-8078-430028A5351E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4B09EE23-0672-4FE0-8078-430028A5351E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4B09EE23-0672-4FE0-8078-430028A5351E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4B09EE23-0672-4FE0-8078-430028A5351E}.Debug|x64.Build.0 = Debug|Any CPU
+ {4B09EE23-0672-4FE0-8078-430028A5351E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4B09EE23-0672-4FE0-8078-430028A5351E}.Debug|x86.Build.0 = Debug|Any CPU
+ {4B09EE23-0672-4FE0-8078-430028A5351E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4B09EE23-0672-4FE0-8078-430028A5351E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4B09EE23-0672-4FE0-8078-430028A5351E}.Release|x64.ActiveCfg = Release|Any CPU
+ {4B09EE23-0672-4FE0-8078-430028A5351E}.Release|x64.Build.0 = Release|Any CPU
+ {4B09EE23-0672-4FE0-8078-430028A5351E}.Release|x86.ActiveCfg = Release|Any CPU
+ {4B09EE23-0672-4FE0-8078-430028A5351E}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Data/TicketingDbContext.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Data/TicketingDbContext.cs
new file mode 100644
index 0000000000..33431ebab6
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Data/TicketingDbContext.cs
@@ -0,0 +1,22 @@
+using EventTicketing.Domain;
+using Microsoft.EntityFrameworkCore;
+
+namespace EventTicketing.Data;
+
+public sealed class TicketingDbContext(DbContextOptions options)
+ : DbContext(options)
+{
+ public DbSet Events => Set();
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity(builder =>
+ {
+ builder.Property(e => e.Name).HasMaxLength(200);
+
+ builder.HasData(
+ new { Id = 1, Name = "Vertical Slice Live", Capacity = 100, TicketsSold = 0 },
+ new { Id = 2, Name = "Tiny Jazz Club Night", Capacity = 2, TicketsSold = 0 });
+ });
+ }
+}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Domain/Event.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Domain/Event.cs
new file mode 100644
index 0000000000..8c2b7c2eda
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Domain/Event.cs
@@ -0,0 +1,37 @@
+namespace EventTicketing.Domain;
+
+public sealed class Event
+{
+ private Event(string name, int capacity)
+ {
+ Name = name;
+ Capacity = capacity;
+ }
+
+ public int Id { get; private set; }
+ public string Name { get; private set; }
+ public int Capacity { get; private set; }
+ public int TicketsSold { get; private set; }
+
+ public int TicketsLeft => Capacity - TicketsSold;
+
+ public static Event Create(string name, int capacity)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(name);
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity);
+
+ return new Event(name, capacity);
+ }
+
+ public Result Reserve(int quantity)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(quantity);
+
+ if (quantity > TicketsLeft)
+ return EventErrors.SoldOut(TicketsLeft, quantity);
+
+ TicketsSold += quantity;
+
+ return Result.Success();
+ }
+}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Domain/EventErrors.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Domain/EventErrors.cs
new file mode 100644
index 0000000000..24d232a0ca
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Domain/EventErrors.cs
@@ -0,0 +1,10 @@
+namespace EventTicketing.Domain;
+
+public static class EventErrors
+{
+ public static Error NotFound(int eventId) =>
+ new("Event.NotFound", $"Event {eventId} was not found.", ErrorType.NotFound);
+
+ public static Error SoldOut(int ticketsLeft, int requested) =>
+ new("Event.SoldOut", $"Only {ticketsLeft} tickets left, {requested} requested.", ErrorType.Conflict);
+}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Domain/Result.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Domain/Result.cs
new file mode 100644
index 0000000000..7f406bb586
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Domain/Result.cs
@@ -0,0 +1,39 @@
+namespace EventTicketing.Domain;
+
+public enum ErrorType
+{
+ NotFound,
+ Conflict
+}
+
+public sealed record Error(string Code, string Description, ErrorType Type);
+
+public class Result
+{
+ protected Result(Error? error) => Error = error;
+
+ public Error? Error { get; }
+ public bool IsSuccess => Error is null;
+
+ public static Result Success() => new(null);
+
+ public static implicit operator Result(Error error) => new(error);
+}
+
+public sealed class Result : Result
+{
+ private readonly TValue? _value;
+
+ private Result(TValue? value, Error? error)
+ : base(error) => _value = value;
+
+ public TValue Value => IsSuccess
+ ? _value!
+ : throw new InvalidOperationException("A failed result has no value.");
+
+ public static implicit operator Result(TValue value) =>
+ new(value, null);
+
+ public static implicit operator Result(Error error) =>
+ new(default, error);
+}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/EventTicketing.csproj b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/EventTicketing.csproj
new file mode 100644
index 0000000000..9361cc4861
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/EventTicketing.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/EventTicketing.http b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/EventTicketing.http
new file mode 100644
index 0000000000..09a304f51a
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/EventTicketing.http
@@ -0,0 +1,37 @@
+GET http://localhost:5000/api/events/1
+
+###
+
+POST http://localhost:5000/api/events/1/reservations
+Content-Type: application/json
+
+{ "quantity": 3 }
+
+###
+
+POST http://localhost:5000/api/events/2/reservations
+Content-Type: application/json
+
+{ "quantity": 3 }
+
+###
+
+POST http://localhost:5000/api/events/1/reservations
+Content-Type: application/json
+
+{ "quantity": 0 }
+
+###
+
+GET http://localhost:5000/api/events/99
+
+###
+
+POST http://localhost:5000/api/events/2/reservations
+Content-Type: application/json
+
+{ "quantity": 2 }
+
+###
+
+GET http://localhost:5000/api/events
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Features/Events/GetEventAvailability.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Features/Events/GetEventAvailability.cs
new file mode 100644
index 0000000000..90df16bb21
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Features/Events/GetEventAvailability.cs
@@ -0,0 +1,39 @@
+using EventTicketing.Data;
+using EventTicketing.Domain;
+using Microsoft.EntityFrameworkCore;
+
+namespace EventTicketing.Features.Events;
+
+public static class GetEventAvailability
+{
+ public sealed record Response(int EventId, string Name, int TicketsLeft);
+
+ public sealed class Handler(TicketingDbContext dbContext)
+ {
+ public async Task> HandleAsync(
+ int eventId, CancellationToken cancellationToken = default)
+ {
+ var availability = await dbContext.Events
+ .AsNoTracking()
+ .Where(e => e.Id == eventId)
+ .Select(e => new Response(e.Id, e.Name, e.Capacity - e.TicketsSold))
+ .FirstOrDefaultAsync(cancellationToken);
+
+ if (availability is null)
+ return EventErrors.NotFound(eventId);
+
+ return availability;
+ }
+ }
+
+ public static void MapEndpoint(IEndpointRouteBuilder app) =>
+ app.MapGet("/{eventId:int}", async (
+ int eventId,
+ Handler handler,
+ CancellationToken cancellationToken) =>
+ {
+ var result = await handler.HandleAsync(eventId, cancellationToken);
+
+ return result.IsSuccess ? Results.Ok(result.Value) : result.ToProblem();
+ });
+}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Features/Events/ListAvailableEvents.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Features/Events/ListAvailableEvents.cs
new file mode 100644
index 0000000000..15bb2ac737
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Features/Events/ListAvailableEvents.cs
@@ -0,0 +1,17 @@
+using EventTicketing.Data;
+using Microsoft.EntityFrameworkCore;
+
+namespace EventTicketing.Features.Events;
+
+public static class ListAvailableEvents
+{
+ public sealed record AvailableEvent(int EventId, string Name, int TicketsLeft);
+
+ public static void MapEndpoint(IEndpointRouteBuilder app) =>
+ app.MapGet("/", async (TicketingDbContext dbContext, CancellationToken cancellationToken) =>
+ await dbContext.Events
+ .AsNoTracking()
+ .Where(e => e.TicketsSold < e.Capacity)
+ .Select(e => new AvailableEvent(e.Id, e.Name, e.Capacity - e.TicketsSold))
+ .ToListAsync(cancellationToken));
+}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Features/Reservations/ReserveTickets.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Features/Reservations/ReserveTickets.cs
new file mode 100644
index 0000000000..b9efe5949e
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Features/Reservations/ReserveTickets.cs
@@ -0,0 +1,44 @@
+using System.ComponentModel.DataAnnotations;
+using EventTicketing.Data;
+using EventTicketing.Domain;
+using Microsoft.EntityFrameworkCore;
+
+namespace EventTicketing.Features.Reservations;
+
+public static class ReserveTickets
+{
+ public sealed record Request([property: Range(1, 20)] int Quantity);
+
+ public sealed record Response(int EventId, int TicketsReserved, int TicketsLeft);
+
+ public sealed class Handler(TicketingDbContext dbContext)
+ {
+ public async Task> HandleAsync(
+ int eventId, int quantity, CancellationToken cancellationToken = default)
+ {
+ var ev = await dbContext.Events.FirstOrDefaultAsync(e => e.Id == eventId, cancellationToken);
+ if (ev is null)
+ return EventErrors.NotFound(eventId);
+
+ var reservation = ev.Reserve(quantity);
+ if (!reservation.IsSuccess)
+ return reservation.Error!;
+
+ await dbContext.SaveChangesAsync(cancellationToken);
+
+ return new Response(ev.Id, quantity, ev.TicketsLeft);
+ }
+ }
+
+ public static void MapEndpoint(IEndpointRouteBuilder app) =>
+ app.MapPost("/{eventId:int}/reservations", async (
+ int eventId,
+ Request request,
+ Handler handler,
+ CancellationToken cancellationToken) =>
+ {
+ var result = await handler.HandleAsync(eventId, request.Quantity, cancellationToken);
+
+ return result.IsSuccess ? Results.Ok(result.Value) : result.ToProblem();
+ });
+}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Program.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Program.cs
new file mode 100644
index 0000000000..a6cac08a41
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Program.cs
@@ -0,0 +1,33 @@
+using EventTicketing.Data;
+using EventTicketing.Features.Events;
+using EventTicketing.Features.Reservations;
+using Microsoft.EntityFrameworkCore;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddDbContext(options =>
+ options.UseSqlite(builder.Configuration.GetConnectionString("Ticketing")));
+
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+builder.Services.AddProblemDetails();
+builder.Services.AddValidation();
+
+var app = builder.Build();
+
+app.UseExceptionHandler();
+
+using (var scope = app.Services.CreateScope())
+{
+ var dbContext = scope.ServiceProvider.GetRequiredService();
+ dbContext.Database.EnsureCreated();
+}
+
+var events = app.MapGroup("/api/events");
+
+GetEventAvailability.MapEndpoint(events);
+ReserveTickets.MapEndpoint(events);
+ListAvailableEvents.MapEndpoint(events);
+
+app.Run();
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Properties/launchSettings.json b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Properties/launchSettings.json
similarity index 50%
rename from csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Properties/launchSettings.json
rename to csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Properties/launchSettings.json
index b3107716ee..b0ac7136aa 100644
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Properties/launchSettings.json
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/Properties/launchSettings.json
@@ -1,28 +1,20 @@
-{
+{
"$schema": "https://json.schemastore.org/launchsettings.json",
- "iisSettings": {
- "windowsAuthentication": false,
- "anonymousAuthentication": true,
- "iisExpress": {
- "applicationUrl": "http://localhost:47567",
- "sslPort": 0
- }
- },
"profiles": {
- "VerticalSliceArchitecture": {
+ "http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
- "launchUrl": "swagger",
- "applicationUrl": "https://localhost:5216",
+ "applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
- "IIS Express": {
- "commandName": "IISExpress",
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
"launchBrowser": true,
- "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:7052;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/ResultExtensions.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/ResultExtensions.cs
new file mode 100644
index 0000000000..6b97d710fb
--- /dev/null
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/ResultExtensions.cs
@@ -0,0 +1,21 @@
+using EventTicketing.Domain;
+
+namespace EventTicketing;
+
+public static class ResultExtensions
+{
+ public static IResult ToProblem(this Result result)
+ {
+ var error = result.Error
+ ?? throw new InvalidOperationException("A successful result is not a problem.");
+
+ var statusCode = error.Type switch
+ {
+ ErrorType.NotFound => StatusCodes.Status404NotFound,
+ ErrorType.Conflict => StatusCodes.Status409Conflict,
+ _ => StatusCodes.Status500InternalServerError
+ };
+
+ return TypedResults.Problem(statusCode: statusCode, title: error.Code, detail: error.Description);
+ }
+}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/appsettings.Development.json b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/appsettings.Development.json
similarity index 100%
rename from csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/appsettings.Development.json
rename to csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/appsettings.Development.json
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/appsettings.json b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/appsettings.json
similarity index 66%
rename from csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/appsettings.json
rename to csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/appsettings.json
index 10f68b8c8b..f0d3f98f07 100644
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/appsettings.json
+++ b/csharp-architectural-patterns/VerticalSliceArchitecture/EventTicketing/appsettings.json
@@ -1,4 +1,7 @@
{
+ "ConnectionStrings": {
+ "Ticketing": "Data Source=tickets.db"
+ },
"Logging": {
"LogLevel": {
"Default": "Information",
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/Tests/ConsoleServiceTest.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/Tests/ConsoleServiceTest.cs
deleted file mode 100644
index 54b0885dd1..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/Tests/ConsoleServiceTest.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-namespace Tests
-{
- public class ConsoleServiceTest
- {
- private DataContext _context;
-
- [Fact]
- public async Task WhenGetAllConsoles_ThenConsolesAreReturnedSuccessfully()
- {
- var options = new DbContextOptionsBuilder()
- .UseInMemoryDatabase("consoleDB1")
- .Options;
-
- _context = new DataContext(options);
-
- _context.Consoles.Add(new GameConsole
- {
- Id = 1,
- Name = "Xbox Series X",
- Manufacturer = "Microsoft"
- });
-
- await _context.SaveChangesAsync();
-
- //When
- var consoles = await new ConsoleService(_context).GetAllConsolesAsync();
-
- //Then
- Assert.NotNull(consoles);
-
- Assert.True(consoles.Any());
- }
-
- [Fact]
- public async Task WhenGetConsoleById_ThenSpecificConsoleIsReturned()
- {
- var options = new DbContextOptionsBuilder()
- .UseInMemoryDatabase("consoleDB2")
- .Options;
-
- _context = new DataContext(options);
-
- _context.Consoles.Add(new GameConsole
- {
- Id = 1,
- Name = "Xbox Series X",
- Manufacturer = "Microsoft"
- });
-
- await _context.SaveChangesAsync();
-
- //When
- var console = await new ConsoleService(_context).GetConsoleByIdAsync(1);
-
- await _context.SaveChangesAsync();
-
- //Then
- Assert.NotNull(console);
-
- Assert.True(console.Id == 1);
- }
- }
-}
\ No newline at end of file
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/Tests/GameServiceTest.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/Tests/GameServiceTest.cs
deleted file mode 100644
index bd663da02e..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/Tests/GameServiceTest.cs
+++ /dev/null
@@ -1,124 +0,0 @@
-namespace Tests
-{
- public class GameServiceTest
- {
- private DataContext _context;
-
- [Fact]
- public async Task WhenGameIsAdded_GameCreatedSuccessfully()
- {
- var options = new DbContextOptionsBuilder()
- .UseInMemoryDatabase("gameDB1")
- .Options;
-
- _context = new DataContext(options);
-
- _context.Consoles.Add(new GameConsole
- {
- Id = 1,
- Name = "Xbox Series X",
- Manufacturer = "Microsoft"
- });
-
- //When
- new GameService(_context).AddGameToConsole(1, new Game { Id = 10, Name = "Fighting Simulator", Publisher = "Best Fights Corp" });
-
- await _context.SaveChangesAsync();
-
- //Then
- Assert.True(await _context.Games.FirstOrDefaultAsync() != null);
- }
-
- [Fact]
- public async Task WhenGetAllGames_ThenGamesAreReturnedSuccessfully()
- {
- var options = new DbContextOptionsBuilder()
- .UseInMemoryDatabase("gameDB2")
- .Options;
-
- _context = new DataContext(options);
-
- _context.Consoles.Add(new GameConsole
- {
- Id = 1,
- Name = "Xbox Series X",
- Manufacturer = "Microsoft"
- });
-
- new GameService(_context).AddGameToConsole(1, new Game { Id = 10, Name = "Fighting Simulator", Publisher = "Best Fights Corp" });
-
- await _context.SaveChangesAsync();
-
- //When
- var games = await new GameService(_context).GetAllGamesAsync(1);
-
- //Then
- Assert.NotNull(games);
-
- Assert.True(games.Any());
- }
-
- [Fact]
- public async Task WhenGetGameByID_ThenSpecifiedGameIsReturnedSuccessfully()
- {
- var options = new DbContextOptionsBuilder()
- .UseInMemoryDatabase("gameDB3")
- .Options;
-
- _context = new DataContext(options);
-
- _context.Consoles.Add(new GameConsole
- {
- Id = 1,
- Name = "Xbox Series X",
- Manufacturer = "Microsoft"
- });
-
- new GameService(_context).AddGameToConsole(1, new Game { Id = 12, Name = "Running Simulator", Publisher = "Best Runs Corp" });
-
- await _context.SaveChangesAsync();
-
- //When
- var game = await new GameService(_context).GetGameAsync(1, 12);
-
- //Then
- Assert.NotNull(game);
-
- Assert.True(game.Id == 12);
- }
-
- [Fact]
- public async Task WhenDeletingAGame_ThenSpecifiedGameIsDeletedSuccessfully()
- {
- var options = new DbContextOptionsBuilder()
- .UseInMemoryDatabase("gameDB4")
- .Options;
-
- _context = new DataContext(options);
-
- _context.Consoles.Add(new GameConsole
- {
- Id = 1,
- Name = "Xbox Series X",
- Manufacturer = "Microsoft"
- });
-
- var newGame = new Game { Id = 13, Name = "Game Dev Simulator", Publisher = "Worst Games Corp" };
-
- new GameService(_context).AddGameToConsole(1, newGame);
-
- await _context.SaveChangesAsync();
-
- //When
- new GameService(_context).DeleteGame(newGame);
-
- await _context.SaveChangesAsync();
-
- //Then
-
- var deletedGame = await _context.Games.FirstOrDefaultAsync(x => x.Id == 13);
-
- Assert.Null(deletedGame);
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/Tests/Tests.csproj b/csharp-architectural-patterns/VerticalSliceArchitecture/Tests/Tests.csproj
deleted file mode 100644
index 0f65eb1ff5..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/Tests/Tests.csproj
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
- net8.0
- enable
- enable
-
- false
-
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
-
-
-
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/Tests/Usings.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/Tests/Usings.cs
deleted file mode 100644
index 26a7334fba..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/Tests/Usings.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-global using Microsoft.EntityFrameworkCore;
-global using VerticalSliceArchitecture.Data;
-global using VerticalSliceArchitecture.Domain;
-global using VerticalSliceArchitecture.Features.Consoles;
-global using VerticalSliceArchitecture.Features.Games;
-global using Xunit;
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture.sln b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture.sln
deleted file mode 100644
index 0c9400484e..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture.sln
+++ /dev/null
@@ -1,31 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.2.32516.85
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VerticalSliceArchitecture", "VerticalSliceArchitecture\VerticalSliceArchitecture.csproj", "{B461B03F-664F-422A-993B-79AC4C6FCF1D}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{0E1829EA-90EC-47FC-B7F9-47B4C31AA54B}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {B461B03F-664F-422A-993B-79AC4C6FCF1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B461B03F-664F-422A-993B-79AC4C6FCF1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B461B03F-664F-422A-993B-79AC4C6FCF1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B461B03F-664F-422A-993B-79AC4C6FCF1D}.Release|Any CPU.Build.0 = Release|Any CPU
- {0E1829EA-90EC-47FC-B7F9-47B4C31AA54B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0E1829EA-90EC-47FC-B7F9-47B4C31AA54B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0E1829EA-90EC-47FC-B7F9-47B4C31AA54B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0E1829EA-90EC-47FC-B7F9-47B4C31AA54B}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {938CAD25-5CDA-4BEB-A011-3EA89D816224}
- EndGlobalSection
-EndGlobal
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Data/DataContext.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Data/DataContext.cs
deleted file mode 100644
index fff48a5d33..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Data/DataContext.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-using VerticalSliceArchitecture.Domain;
-
-namespace VerticalSliceArchitecture.Data
-{
- public class DataContext : DbContext
- {
- public DataContext(DbContextOptions options) : base(options) { }
- public DbSet Consoles { get; set; }
- public DbSet Games { get; set; }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Data/Seed.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Data/Seed.cs
deleted file mode 100644
index 81df2bb323..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Data/Seed.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using VerticalSliceArchitecture.Domain;
-
-namespace VerticalSliceArchitecture.Data
-{
- public class Seed
- {
- public void SeedData(DataContext context)
- {
- //Seeding Consoles
-
- context.Consoles.Add(new GameConsole
- {
- Id = 1,
- Name = "Xbox Series X",
- Manufacturer = "Microsoft"
- });
-
- context.Consoles.Add(new GameConsole
- {
- Id = 2,
- Name = "PlayStation 5",
- Manufacturer = "Sony"
- });
-
- context.Consoles.Add(new GameConsole
- {
- Id = 3,
- Name = "Nintendo Switch",
- Manufacturer = "Nintendo"
- });
-
- //Seeding Games
-
- context.Games.Add(new Game
- {
- Id = 10,
- Name = "Horizon Forbidden West",
- Publisher = "Sony Interactive Entertainment",
- ConsoleId = 2
- });
-
- context.Games.Add(new Game
- {
- Id = 11,
- Name = "Animal Crossing: New Horizons",
- Publisher = "Nintendo",
- ConsoleId = 3
- });
-
- context.Games.Add(new Game
- {
- Id = 12,
- Name = "Halo Infinite",
- Publisher = "Xbox Game Studios",
- ConsoleId = 1
- });
-
- context.SaveChanges();
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Domain/Game.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Domain/Game.cs
deleted file mode 100644
index 6648a7771f..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Domain/Game.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace VerticalSliceArchitecture.Domain
-{
- public class Game
- {
- public int Id { get; set; }
- public string? Name { get; set; }
- public string? Publisher { get; set; }
- public int ConsoleId { get; set; }
- public GameConsole? Console { get; set; }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Domain/GameConsole.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Domain/GameConsole.cs
deleted file mode 100644
index 8372cb0d7e..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Domain/GameConsole.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace VerticalSliceArchitecture.Domain
-{
- public class GameConsole
- {
- public int Id { get; set; }
- public string? Name { get; set; }
- public string? Manufacturer { get; set; }
- public ICollection? Games { get; set; }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/ConsoleService.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/ConsoleService.cs
deleted file mode 100644
index 8f795e3243..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/ConsoleService.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-using VerticalSliceArchitecture.Data;
-using VerticalSliceArchitecture.Domain;
-
-namespace VerticalSliceArchitecture.Features.Consoles
-{
- public class ConsoleService : IConsoleService
- {
- private readonly DataContext _context;
-
- public ConsoleService(DataContext context)
- {
- _context = context;
- }
-
- public async Task> GetAllConsolesAsync()
- {
- return await _context.Consoles
- .OrderBy(x => x.Id)
- .ToListAsync();
- }
-
- public async Task GetConsoleByIdAsync(int consoleId)
- {
- return await _context.Consoles
- .FirstOrDefaultAsync(x => x.Id == consoleId);
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/ConsolesController.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/ConsolesController.cs
deleted file mode 100644
index c75a8c6d88..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/ConsolesController.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using MediatR;
-using Microsoft.AspNetCore.Mvc;
-
-namespace VerticalSliceArchitecture.Features.Consoles
-{
- [Route("api/[controller]")]
- [ApiController]
- public class ConsolesController : ControllerBase
- {
- private readonly IMediator _mediator;
- public ConsolesController(IMediator mediator)
- {
- _mediator = mediator;
- }
-
- [HttpGet]
- public async Task>> GetConsolesAsync()
- {
- var result = await _mediator.Send(new GetAllConsoles.GetConsolesQuery());
-
- if (result is null)
- return NotFound();
-
- return Ok(result);
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/GetAllConsoles.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/GetAllConsoles.cs
deleted file mode 100644
index 9857427aed..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/GetAllConsoles.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using AutoMapper;
-using MediatR;
-using VerticalSliceArchitecture.ServiceManager;
-
-namespace VerticalSliceArchitecture.Features.Consoles
-{
- public class GetAllConsoles
- {
- //Input
- public class GetConsolesQuery : IRequest> { }
-
- //Output
- public class ConsoleResult
- {
- public int Id { get; set; }
- public string? Name { get; set; }
- public string? Manufacturer { get; set; }
- }
-
- //Handler
- public class Handler : IRequestHandler>
- {
- private readonly IServiceManager _serviceManager;
- private readonly IMapper _mapper;
-
- public Handler(IServiceManager serviceManager, IMapper mapper)
- {
- _serviceManager = serviceManager;
- _mapper = mapper;
- }
-
- public async Task> Handle(GetConsolesQuery request, CancellationToken cancellationToken)
- {
- var consoles = await _serviceManager.Console.GetAllConsolesAsync();
- var results = _mapper.Map>(consoles);
-
- return results;
- }
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/IConsoleService.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/IConsoleService.cs
deleted file mode 100644
index 18b3f810ac..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/IConsoleService.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using VerticalSliceArchitecture.Domain;
-
-namespace VerticalSliceArchitecture.Features.Consoles
-{
- public interface IConsoleService
- {
- Task> GetAllConsolesAsync();
- Task GetConsoleByIdAsync(int consoleId);
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/MapperProfile.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/MapperProfile.cs
deleted file mode 100644
index e18ebbff8a..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Consoles/MapperProfile.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using AutoMapper;
-using VerticalSliceArchitecture.Domain;
-
-namespace VerticalSliceArchitecture.Features.Consoles
-{
- public class MapperProfile : Profile
- {
- public MapperProfile()
- {
- CreateMap();
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/AddGameToConsole.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/AddGameToConsole.cs
deleted file mode 100644
index 57907817fd..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/AddGameToConsole.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using AutoMapper;
-using MediatR;
-using VerticalSliceArchitecture.Domain;
-using VerticalSliceArchitecture.Features.Games.Exceptions;
-using VerticalSliceArchitecture.ServiceManager;
-
-namespace VerticalSliceArchitecture.Features.Games
-{
- public class AddGameToConsole
- {
- //Input
- public class AddGameCommand : IRequest
- {
- public string? Name { get; set; }
- public string? Publisher { get; set; }
- public int ConsoleId { get; set; }
- }
-
- //Output
- public class GameResult
- {
- public int Id { get; set; }
- public string? Name { get; set; }
- public string? Publisher { get; set; }
- public int ConsoleId { get; set; }
- }
-
- //Handler
- public class Handler : IRequestHandler
- {
- private readonly IServiceManager _serviceManager;
- private readonly IMapper _mapper;
-
- public Handler(IServiceManager serviceManager, IMapper mapper)
- {
- _serviceManager = serviceManager;
- _mapper = mapper;
- }
-
- public async Task Handle(AddGameCommand request, CancellationToken cancellationToken)
- {
- var console = await _serviceManager.Console.GetConsoleByIdAsync(request.ConsoleId);
-
- if (console is null)
- throw new NoConsoleExistsException(request.ConsoleId);
-
- var game = new Game()
- {
- Name = request.Name,
- Publisher = request.Publisher,
- ConsoleId = request.ConsoleId
- };
-
- _serviceManager.Game.AddGameToConsole(request.ConsoleId, game);
-
- await _serviceManager.SaveAsync();
-
- var result = _mapper.Map(game);
-
- return result;
- }
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/Exceptions/NoConsoleExistsException.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/Exceptions/NoConsoleExistsException.cs
deleted file mode 100644
index 0611c69373..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/Exceptions/NoConsoleExistsException.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace VerticalSliceArchitecture.Features.Games.Exceptions
-{
- public class NoConsoleExistsException : Exception
- {
- public NoConsoleExistsException(int consoleId) : base($"Console with id: {consoleId} doesn't exist.") { }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/Exceptions/NoGameExistsException.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/Exceptions/NoGameExistsException.cs
deleted file mode 100644
index 98fb65fceb..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/Exceptions/NoGameExistsException.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace VerticalSliceArchitecture.Features.Games.Exceptions
-{
- public class NoGameExistsException : Exception
- {
- public NoGameExistsException(int consoleId, int gameId) : base($"Game with id: {gameId} doesn't exist for console id {consoleId}")
- {
- ConsoleId = consoleId;
- GameId = gameId;
- }
-
- public int ConsoleId { get; set; }
- public int GameId { get; set; }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/GameService.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/GameService.cs
deleted file mode 100644
index 36ce4932bf..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/GameService.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-using VerticalSliceArchitecture.Data;
-using VerticalSliceArchitecture.Domain;
-
-namespace VerticalSliceArchitecture.Features.Games
-{
- public class GameService : IGameService
- {
- private readonly DataContext _context;
-
- public GameService(DataContext context)
- {
- _context = context;
- }
- public void AddGameToConsole(int consoleId, Game game)
- {
- game.ConsoleId = consoleId;
-
- _context.Games.Add(game);
- }
-
- public void DeleteGame(Game game)
- {
- _context.Games.Remove(game);
- }
-
- public async Task> GetAllGamesAsync(int consoleId)
- {
- return await _context.Games
- .Where(x => x.ConsoleId == consoleId)
- .ToListAsync();
- }
-
- public async Task GetGameAsync(int consoleId, int gameId)
- {
- return await _context.Games
- .FirstOrDefaultAsync(x => x.ConsoleId == consoleId && x.Id == gameId);
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/GamesController.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/GamesController.cs
deleted file mode 100644
index 92097d8b28..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/GamesController.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-using MediatR;
-using Microsoft.AspNetCore.Mvc;
-using VerticalSliceArchitecture.Features.Games.Exceptions;
-using static VerticalSliceArchitecture.Features.Games.GetAllGamesForConsole;
-using static VerticalSliceArchitecture.Features.Games.RemoveGameFromConsole;
-using static VerticalSliceArchitecture.Features.Games.UpdateGameForConsole;
-
-namespace VerticalSliceArchitecture.Features.Games
-{
- [Route("api/[controller]")]
- [ApiController]
- public class GamesController : ControllerBase
- {
- private readonly IMediator _mediator;
-
- public GamesController(IMediator mediator)
- {
- _mediator = mediator;
- }
-
- [HttpGet(Name = "GetGamesForConsole")]
- public async Task>> GetGamesForConsole(int consoleId)
- {
- try
- {
- var query = new GetGamesQuery
- {
- ConsoleId = consoleId
- };
-
- var result = await _mediator.Send(query);
-
- return Ok(result);
- }
- catch (NoConsoleExistsException ex)
- {
- return Conflict(new
- {
- ex.Message
- });
- }
- }
-
- [HttpPost]
- public async Task AddGame(AddGameToConsole.AddGameCommand command)
- {
- try
- {
- var result = await _mediator.Send(command);
-
- return CreatedAtRoute("GetGamesForConsole", new { consoleId = result.ConsoleId }, result);
- }
- catch (NoConsoleExistsException ex)
- {
- return Conflict(new
- {
- ex.Message
- });
- }
- }
-
- [HttpPut]
- public async Task UpdateGameForConsole(int consoleId, UpdateGameCommand command)
- {
- try
- {
- command.ConsoleId = consoleId;
-
- await _mediator.Send(command);
-
- return NoContent();
- }
- catch (NoConsoleExistsException ex)
- {
- return Conflict(new
- {
- ex.Message
- });
- }
- catch (NoGameExistsException ex)
- {
- return Conflict(new
- {
- ex.Message,
- ex.ConsoleId,
- ex.GameId
- });
- }
- }
-
- [HttpDelete]
- public async Task RemoveGameFromConsole(int consoleId, RemoveGameCommand command)
- {
- try
- {
- command.ConsoleId = consoleId;
-
- await _mediator.Send(command);
-
- return NoContent();
- }
- catch (NoConsoleExistsException ex)
- {
- return Conflict(new
- {
- ex.Message
- });
- }
- catch (NoGameExistsException ex)
- {
- return Conflict(new
- {
- ex.Message,
- ex.ConsoleId,
- ex.GameId
- });
- }
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/GetAllGamesForConsole.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/GetAllGamesForConsole.cs
deleted file mode 100644
index 01f966e4da..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/GetAllGamesForConsole.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using AutoMapper;
-using MediatR;
-using VerticalSliceArchitecture.Features.Games.Exceptions;
-using VerticalSliceArchitecture.ServiceManager;
-
-namespace VerticalSliceArchitecture.Features.Games
-{
- public class GetAllGamesForConsole
- {
- public class GetGamesQuery : IRequest>
- {
- public int ConsoleId { get; set; }
- }
-
- public class GameResult
- {
- public int Id { get; set; }
- public string? Name { get; set; }
- public string? Publisher { get; set; }
- }
-
- public class Handler : IRequestHandler>
- {
- private readonly IServiceManager _serviceManager;
- private readonly IMapper _mapper;
-
- public Handler(IServiceManager serviceManager, IMapper mapper)
- {
- _serviceManager = serviceManager;
- _mapper = mapper;
- }
-
- public async Task> Handle(GetGamesQuery request, CancellationToken cancellationToken)
- {
- var console = await _serviceManager.Console.GetConsoleByIdAsync(request.ConsoleId);
-
- if (console is null)
- throw new NoConsoleExistsException(request.ConsoleId);
-
- var games = await _serviceManager.Game.GetAllGamesAsync(console.Id);
-
- var result = _mapper.Map>(games);
-
- return result;
- }
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/IGameService.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/IGameService.cs
deleted file mode 100644
index eead99ea99..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/IGameService.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using VerticalSliceArchitecture.Domain;
-
-namespace VerticalSliceArchitecture.Features.Games
-{
- public interface IGameService
- {
- Task> GetAllGamesAsync(int consoleId);
- Task GetGameAsync(int consoleId, int gameId);
- void AddGameToConsole(int consoleId, Game game);
- void DeleteGame(Game game);
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/MapperProfile.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/MapperProfile.cs
deleted file mode 100644
index 0c6f42f952..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/MapperProfile.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using AutoMapper;
-using VerticalSliceArchitecture.Domain;
-
-namespace VerticalSliceArchitecture.Features.Games
-{
- public class MapperProfile : Profile
- {
- public MapperProfile()
- {
- CreateMap();
- CreateMap();
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/RemoveGameFromConsole.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/RemoveGameFromConsole.cs
deleted file mode 100644
index 1e5867c216..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/RemoveGameFromConsole.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using MediatR;
-using VerticalSliceArchitecture.Features.Games.Exceptions;
-using VerticalSliceArchitecture.ServiceManager;
-
-namespace VerticalSliceArchitecture.Features.Games
-{
- public class RemoveGameFromConsole
- {
- public class RemoveGameCommand : IRequest
- {
- public int ConsoleId { get; set; }
- public int GameId { get; set; }
- }
-
- public class Handler : IRequestHandler
- {
- private readonly IServiceManager _serviceManager;
-
- public Handler(IServiceManager serviceManager)
- {
- _serviceManager = serviceManager;
- }
-
- public async Task Handle(RemoveGameCommand request, CancellationToken cancellationToken)
- {
- var console = await _serviceManager.Console.GetConsoleByIdAsync(request.ConsoleId);
-
- if (console is null)
- throw new NoConsoleExistsException(request.ConsoleId);
-
- var game = await _serviceManager.Game.GetGameAsync(request.ConsoleId, request.GameId);
-
- if (game is null)
- throw new NoGameExistsException(request.ConsoleId, request.GameId);
-
- _serviceManager.Game.DeleteGame(game);
-
- await _serviceManager.SaveAsync();
-
- return;
- }
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/UpdateGameForConsole.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/UpdateGameForConsole.cs
deleted file mode 100644
index 472ac9d6ad..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Features/Games/UpdateGameForConsole.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using AutoMapper;
-using MediatR;
-using VerticalSliceArchitecture.Features.Games.Exceptions;
-using VerticalSliceArchitecture.ServiceManager;
-
-namespace VerticalSliceArchitecture.Features.Games
-{
- public class UpdateGameForConsole
- {
- public class UpdateGameCommand : IRequest
- {
- public int Id { get; set; }
- public string? Name { get; set; }
- public string? Publisher { get; set; }
- public int ConsoleId { get; set; }
- }
-
- public class Handler : IRequestHandler
- {
- private readonly IServiceManager _serviceManager;
-
- public Handler(IServiceManager serviceManager)
- {
- _serviceManager = serviceManager;
- }
-
- public async Task Handle(UpdateGameCommand request, CancellationToken cancellationToken)
- {
- var console = await _serviceManager.Console.GetConsoleByIdAsync(request.ConsoleId);
-
- if (console is null)
- throw new NoConsoleExistsException(request.ConsoleId);
-
- var game = await _serviceManager.Game.GetGameAsync(request.ConsoleId, request.Id);
-
- if (game is null)
- throw new NoGameExistsException(request.ConsoleId, request.Id);
-
- game.Name = request.Name!;
- game.Publisher = request.Publisher!;
-
- await _serviceManager.SaveAsync();
-
- return;
- }
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Program.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Program.cs
deleted file mode 100644
index 6016bbdfc0..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/Program.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using MediatR;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using VerticalSliceArchitecture.Data;
-using VerticalSliceArchitecture.ServiceManager;
-
-var builder = WebApplication.CreateBuilder(args);
-
-// Add services to the container.
-
-builder.Services.AddAutoMapper(typeof(Program).Assembly);
-builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));
-
-builder.Services.AddDbContext(options =>
-{
- options.UseInMemoryDatabase("GamingDB")
- .ConfigureWarnings(builder => builder.Ignore(InMemoryEventId.TransactionIgnoredWarning));
-});
-
-builder.Services.AddScoped();
-
-builder.Services.AddControllers();
-// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
-builder.Services.AddEndpointsApiExplorer();
-builder.Services.AddSwaggerGen();
-
-var app = builder.Build();
-
-// Configure the HTTP request pipeline.
-if (app.Environment.IsDevelopment())
-{
- app.UseSwagger();
- app.UseSwaggerUI();
-}
-
-using (var scope = app.Services.CreateScope())
-{
- var dataContext = scope.ServiceProvider.GetRequiredService();
- new Seed().SeedData(dataContext);
-}
-
-app.UseAuthorization();
-
-app.MapControllers();
-
-app.Run();
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/ServiceManager/IServiceManager.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/ServiceManager/IServiceManager.cs
deleted file mode 100644
index ba4e3aec7a..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/ServiceManager/IServiceManager.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using VerticalSliceArchitecture.Features.Consoles;
-using VerticalSliceArchitecture.Features.Games;
-
-namespace VerticalSliceArchitecture.ServiceManager
-{
- public interface IServiceManager
- {
- IConsoleService Console { get; }
- IGameService Game { get; }
- Task SaveAsync();
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/ServiceManager/ServiceManager.cs b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/ServiceManager/ServiceManager.cs
deleted file mode 100644
index 1d888665c4..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/ServiceManager/ServiceManager.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using VerticalSliceArchitecture.Data;
-using VerticalSliceArchitecture.Features.Consoles;
-using VerticalSliceArchitecture.Features.Games;
-
-namespace VerticalSliceArchitecture.ServiceManager
-{
- public class ServiceManager : IServiceManager
- {
- private readonly DataContext _context;
- private IConsoleService _consoleService;
- private IGameService _gameService;
-
- public ServiceManager(DataContext context)
- {
- _context = context;
- }
-
- public IConsoleService Console
- {
- get
- {
- if (_consoleService == null)
- _consoleService = new ConsoleService(_context);
-
- return _consoleService;
- }
- }
-
- public IGameService Game
- {
- get
- {
- if (_gameService == null)
- _gameService = new GameService(_context);
-
- return _gameService;
- }
- }
-
- public Task SaveAsync()
- {
- return _context.SaveChangesAsync();
- }
- }
-}
diff --git a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/VerticalSliceArchitecture.csproj b/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/VerticalSliceArchitecture.csproj
deleted file mode 100644
index 365dcd1c6a..0000000000
--- a/csharp-architectural-patterns/VerticalSliceArchitecture/VerticalSliceArchitecture/VerticalSliceArchitecture.csproj
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- net8.0
- enable
- enable
-
-
-
-
-
-
-
-
-
-