diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/AggregateDesign.Domain.csproj b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/AggregateDesign.Domain.csproj
new file mode 100644
index 0000000000..b760144708
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/AggregateDesign.Domain.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/IOrderRepository.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/IOrderRepository.cs
new file mode 100644
index 0000000000..2ec3d17fc8
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/IOrderRepository.cs
@@ -0,0 +1,10 @@
+namespace AggregateDesign.Domain;
+
+public interface IOrderRepository
+{
+ Task GetByIdAsync(long orderId, CancellationToken cancellationToken = default);
+
+ void Add(Order order);
+
+ Task SaveChangesAsync(CancellationToken cancellationToken = default);
+}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/Order.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/Order.cs
new file mode 100644
index 0000000000..b3ad1b5014
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/Order.cs
@@ -0,0 +1,116 @@
+namespace AggregateDesign.Domain;
+
+public sealed class Order
+{
+ private readonly List _items = [];
+
+ private Order(DateTime createdAtUtc)
+ {
+ CreatedAtUtc = createdAtUtc;
+ Status = OrderStatus.PendingPayment;
+ }
+
+ public long Id { get; private set; }
+ public DateTime CreatedAtUtc { get; private set; }
+ public DateTime? ShippedAtUtc { get; private set; }
+ public decimal PaidAmount { get; private set; }
+ public OrderStatus Status { get; private set; }
+
+ public IReadOnlyCollection Items => _items.AsReadOnly();
+
+ public decimal Total => _items.Sum(item => item.Quantity * item.UnitPrice);
+ public decimal AmountDue => Total - PaidAmount;
+
+ public static Order Create(TimeProvider clock) => new(clock.GetUtcNow().UtcDateTime);
+
+ public Result AddItem(string name, int quantity, decimal unitPrice)
+ {
+ if (PaidAmount > 0)
+ return OrderErrors.ItemsLocked;
+
+ if (FindItem(name) is not null)
+ return OrderErrors.DuplicateItem(name);
+
+ _items.Add(new OrderItem(name, quantity, unitPrice));
+
+ return Result.Success();
+ }
+
+ public Result RemoveItem(string name)
+ {
+ if (PaidAmount > 0)
+ return OrderErrors.ItemsLocked;
+
+ var item = FindItem(name);
+ if (item is null)
+ return OrderErrors.ItemNotFound(name);
+
+ _items.Remove(item);
+
+ return Result.Success();
+ }
+
+ public Result AddQuantity(string name, int quantity)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(quantity);
+
+ if (PaidAmount > 0)
+ return OrderErrors.ItemsLocked;
+
+ var item = FindItem(name);
+ if (item is null)
+ return OrderErrors.ItemNotFound(name);
+
+ item.AddQuantity(quantity);
+
+ return Result.Success();
+ }
+
+ public Result WithdrawQuantity(string name, int quantity)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(quantity);
+
+ if (PaidAmount > 0)
+ return OrderErrors.ItemsLocked;
+
+ var item = FindItem(name);
+ if (item is null)
+ return OrderErrors.ItemNotFound(name);
+
+ return item.WithdrawQuantity(quantity);
+ }
+
+ public Result AddPayment(decimal amount)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount);
+
+ if (amount > AmountDue)
+ return OrderErrors.Overpayment(AmountDue, amount);
+
+ PaidAmount += amount;
+
+ if (AmountDue == 0)
+ Status = OrderStatus.ReadyForShipping;
+
+ return Result.Success();
+ }
+
+ public Result Ship(TimeProvider clock)
+ {
+ if (_items.Count == 0)
+ return OrderErrors.Empty;
+
+ if (Status == OrderStatus.InTransit)
+ return OrderErrors.AlreadyShipped;
+
+ if (Status != OrderStatus.ReadyForShipping)
+ return OrderErrors.NotPaid(AmountDue);
+
+ ShippedAtUtc = clock.GetUtcNow().UtcDateTime;
+ Status = OrderStatus.InTransit;
+
+ return Result.Success();
+ }
+
+ private OrderItem? FindItem(string name) => _items.Find(item => item.Name == name);
+}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/OrderErrors.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/OrderErrors.cs
new file mode 100644
index 0000000000..8582e5a8ab
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/OrderErrors.cs
@@ -0,0 +1,28 @@
+namespace AggregateDesign.Domain;
+
+public static class OrderErrors
+{
+ public static Error ItemsLocked =>
+ new("Order.ItemsLocked", "Items can't change once payment has started.", ErrorType.Conflict);
+
+ public static Error DuplicateItem(string name) =>
+ new("Order.DuplicateItem", $"The order already has an item named '{name}'.", ErrorType.Conflict);
+
+ public static Error ItemNotFound(string name) =>
+ new("Order.ItemNotFound", $"The order has no item named '{name}'.", ErrorType.NotFound);
+
+ public static Error LastUnit(string name, int quantity) =>
+ new("Order.LastUnit", $"'{name}' has {quantity} left and needs at least one. Remove the item instead.", ErrorType.Conflict);
+
+ public static Error Overpayment(decimal amountDue, decimal amount) =>
+ new("Order.Overpayment", $"Only {amountDue} is due, but the payment is {amount}.", ErrorType.Conflict);
+
+ public static Error Empty =>
+ new("Order.Empty", "An order with no items can't ship.", ErrorType.Conflict);
+
+ public static Error NotPaid(decimal amountDue) =>
+ new("Order.NotPaid", $"The order can't ship while {amountDue} is still due.", ErrorType.Conflict);
+
+ public static Error AlreadyShipped =>
+ new("Order.AlreadyShipped", "The order has already shipped.", ErrorType.Conflict);
+}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/OrderItem.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/OrderItem.cs
new file mode 100644
index 0000000000..51b51c7590
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/OrderItem.cs
@@ -0,0 +1,31 @@
+namespace AggregateDesign.Domain;
+
+public sealed class OrderItem
+{
+ internal OrderItem(string name, int quantity, decimal unitPrice)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(name);
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(quantity);
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(unitPrice);
+
+ Name = name;
+ Quantity = quantity;
+ UnitPrice = unitPrice;
+ }
+
+ public string Name { get; private set; }
+ public int Quantity { get; private set; }
+ public decimal UnitPrice { get; private set; }
+
+ internal void AddQuantity(int quantity) => Quantity += quantity;
+
+ internal Result WithdrawQuantity(int quantity)
+ {
+ if (quantity >= Quantity)
+ return OrderErrors.LastUnit(Name, Quantity);
+
+ Quantity -= quantity;
+
+ return Result.Success();
+ }
+}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/OrderStatus.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/OrderStatus.cs
new file mode 100644
index 0000000000..3064af3fd4
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/OrderStatus.cs
@@ -0,0 +1,8 @@
+namespace AggregateDesign.Domain;
+
+public enum OrderStatus
+{
+ PendingPayment,
+ ReadyForShipping,
+ InTransit
+}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/Result.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/Result.cs
new file mode 100644
index 0000000000..0946590cbe
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Domain/Result.cs
@@ -0,0 +1,21 @@
+namespace AggregateDesign.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);
+}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Infrastructure/AggregateDesign.Infrastructure.csproj b/csharp-design-patterns/AggregateDesign/AggregateDesign.Infrastructure/AggregateDesign.Infrastructure.csproj
new file mode 100644
index 0000000000..16e1e29bce
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Infrastructure/AggregateDesign.Infrastructure.csproj
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Infrastructure/OrderRepository.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign.Infrastructure/OrderRepository.cs
new file mode 100644
index 0000000000..59e153e088
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Infrastructure/OrderRepository.cs
@@ -0,0 +1,15 @@
+using AggregateDesign.Domain;
+using Microsoft.EntityFrameworkCore;
+
+namespace AggregateDesign.Infrastructure;
+
+public sealed class OrderRepository(OrdersDbContext dbContext) : IOrderRepository
+{
+ public Task GetByIdAsync(long orderId, CancellationToken cancellationToken = default) =>
+ dbContext.Orders.FirstOrDefaultAsync(o => o.Id == orderId, cancellationToken);
+
+ public void Add(Order order) => dbContext.Orders.Add(order);
+
+ public Task SaveChangesAsync(CancellationToken cancellationToken = default) =>
+ dbContext.SaveChangesAsync(cancellationToken);
+}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Infrastructure/OrdersDbContext.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign.Infrastructure/OrdersDbContext.cs
new file mode 100644
index 0000000000..0f9239daa9
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Infrastructure/OrdersDbContext.cs
@@ -0,0 +1,16 @@
+using AggregateDesign.Domain;
+using Microsoft.EntityFrameworkCore;
+
+namespace AggregateDesign.Infrastructure;
+
+public sealed class OrdersDbContext(DbContextOptions options)
+ : DbContext(options)
+{
+ public DbSet Orders => Set();
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity()
+ .OwnsMany(o => o.Items, item => item.HasKey("OrderId", nameof(OrderItem.Name)));
+ }
+}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Tests/AggregateDesign.Tests.csproj b/csharp-design-patterns/AggregateDesign/AggregateDesign.Tests/AggregateDesign.Tests.csproj
new file mode 100644
index 0000000000..2546b5331c
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Tests/AggregateDesign.Tests.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Tests/OrderRepositoryTests.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign.Tests/OrderRepositoryTests.cs
new file mode 100644
index 0000000000..c5d653196c
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Tests/OrderRepositoryTests.cs
@@ -0,0 +1,92 @@
+using AggregateDesign.Domain;
+using AggregateDesign.Infrastructure;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+
+namespace AggregateDesign.Tests;
+
+public sealed class OrderRepositoryTests : IDisposable
+{
+ private const string DutchOven = "Enameled Cast Iron Covered Dutch Oven";
+ private const string Skillet = "Skillet with Red Silicone Hot Handle Holder, 12-inch";
+
+ private readonly SqliteConnection _connection = new("DataSource=:memory:");
+ private readonly DbContextOptions _options;
+
+ public OrderRepositoryTests()
+ {
+ _connection.Open();
+ _options = new DbContextOptionsBuilder().UseSqlite(_connection).Options;
+
+ using var dbContext = new OrdersDbContext(_options);
+ dbContext.Database.EnsureCreated();
+ }
+
+ [Fact]
+ public async Task SavedOrder_LoadsBackWithItsItems()
+ {
+ var orderId = await SaveNewOrderAsync();
+
+ var loaded = await LoadAsync(orderId);
+
+ Assert.NotNull(loaded);
+ Assert.Equal(2, loaded.Items.Count);
+ Assert.Equal(106.25m, loaded.Total);
+ }
+
+ [Fact]
+ public async Task ChangesToALoadedOrder_ReachTheDatabase()
+ {
+ var orderId = await SaveNewOrderAsync();
+
+ await using (var dbContext = new OrdersDbContext(_options))
+ {
+ var repository = new OrderRepository(dbContext);
+ var order = await repository.GetByIdAsync(orderId, TestContext.Current.CancellationToken);
+
+ order!.RemoveItem(DutchOven);
+ order.AddQuantity(Skillet, 1);
+ order.AddPayment(59.80m);
+
+ await repository.SaveChangesAsync(TestContext.Current.CancellationToken);
+ }
+
+ var reloaded = await LoadAsync(orderId);
+
+ var skillet = Assert.Single(reloaded!.Items);
+ Assert.Equal(2, skillet.Quantity);
+ Assert.Equal(59.80m, reloaded.PaidAmount);
+ Assert.Equal(OrderStatus.ReadyForShipping, reloaded.Status);
+ }
+
+ [Fact]
+ public async Task GetByIdAsync_ForAnUnknownId_ReturnsNull()
+ {
+ var loaded = await LoadAsync(42);
+
+ Assert.Null(loaded);
+ }
+
+ private async Task SaveNewOrderAsync()
+ {
+ var order = Order.Create(TimeProvider.System);
+ order.AddItem(DutchOven, 1, 76.35m);
+ order.AddItem(Skillet, 1, 29.90m);
+
+ await using var dbContext = new OrdersDbContext(_options);
+ var repository = new OrderRepository(dbContext);
+ repository.Add(order);
+ await repository.SaveChangesAsync(TestContext.Current.CancellationToken);
+
+ return order.Id;
+ }
+
+ private async Task LoadAsync(long orderId)
+ {
+ await using var dbContext = new OrdersDbContext(_options);
+
+ return await new OrderRepository(dbContext).GetByIdAsync(orderId, TestContext.Current.CancellationToken);
+ }
+
+ public void Dispose() => _connection.Dispose();
+}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.Tests/OrderTests.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign.Tests/OrderTests.cs
new file mode 100644
index 0000000000..f77faed07a
--- /dev/null
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.Tests/OrderTests.cs
@@ -0,0 +1,157 @@
+using AggregateDesign.Domain;
+
+namespace AggregateDesign.Tests;
+
+public class OrderTests
+{
+ private const string Shoes = "Cloudsoft Women's Running Shoes";
+ private const string TShirt = "Gildone Men's Crew T-Shirt";
+
+ private static Order NewOrder()
+ {
+ var order = Order.Create(TimeProvider.System);
+ order.AddItem(Shoes, 1, 59.99m);
+ order.AddItem(TShirt, 3, 18.99m);
+
+ return order;
+ }
+
+ [Fact]
+ public void AddPayment_WhenItCoversTheTotal_MakesTheOrderReadyForShipping()
+ {
+ var order = NewOrder();
+
+ var result = order.AddPayment(116.96m);
+
+ Assert.True(result.IsSuccess);
+ Assert.Equal(OrderStatus.ReadyForShipping, order.Status);
+ }
+
+ [Fact]
+ public void AddPayment_WhenItExceedsTheAmountDue_ReturnsOverpayment()
+ {
+ var order = NewOrder();
+
+ var result = order.AddPayment(200m);
+
+ Assert.Equal("Order.Overpayment", result.Error!.Code);
+ Assert.Equal(0m, order.PaidAmount);
+ }
+
+ [Fact]
+ public void RemoveItem_AfterPaymentHasStarted_ReturnsItemsLocked()
+ {
+ var order = NewOrder();
+ order.AddPayment(100m);
+
+ var result = order.RemoveItem(TShirt);
+
+ Assert.Equal("Order.ItemsLocked", result.Error!.Code);
+ Assert.Equal(116.96m, order.Total);
+ }
+
+ [Fact]
+ public void AddQuantity_AfterFullPayment_ReturnsItemsLocked()
+ {
+ var order = NewOrder();
+ order.AddPayment(order.Total);
+
+ var result = order.AddQuantity(Shoes, 9);
+
+ Assert.Equal("Order.ItemsLocked", result.Error!.Code);
+ Assert.Equal(order.PaidAmount, order.Total);
+ }
+
+ [Fact]
+ public void WithdrawQuantity_WhenItWouldTakeEveryUnit_ReturnsLastUnit()
+ {
+ var order = NewOrder();
+
+ var result = order.WithdrawQuantity(TShirt, 4);
+
+ Assert.Equal("Order.LastUnit", result.Error!.Code);
+ Assert.Equal(3, order.Items.Single(item => item.Name == TShirt).Quantity);
+ }
+
+ [Fact]
+ public void AddQuantity_ForAnUnknownItem_ReturnsItemNotFound()
+ {
+ var order = NewOrder();
+
+ var result = order.AddQuantity("Wool Socks", 2);
+
+ Assert.Equal("Order.ItemNotFound", result.Error!.Code);
+ }
+
+ [Fact]
+ public void AddItem_WithANameAlreadyInTheOrder_ReturnsDuplicateItem()
+ {
+ var order = NewOrder();
+
+ var result = order.AddItem(Shoes, 1, 59.99m);
+
+ Assert.Equal("Order.DuplicateItem", result.Error!.Code);
+ Assert.Equal(2, order.Items.Count);
+ }
+
+ [Fact]
+ public void AddItem_WithZeroQuantity_Throws()
+ {
+ var order = Order.Create(TimeProvider.System);
+
+ Assert.Throws(() => order.AddItem(Shoes, 0, 59.99m));
+ }
+
+ [Fact]
+ public void Ship_WhenNotPaidInFull_ReturnsNotPaid()
+ {
+ var order = NewOrder();
+ order.AddPayment(20m);
+
+ var result = order.Ship(TimeProvider.System);
+
+ Assert.Equal("Order.NotPaid", result.Error!.Code);
+ Assert.Equal(OrderStatus.PendingPayment, order.Status);
+ }
+
+ [Fact]
+ public void Ship_WithNoItems_ReturnsEmpty()
+ {
+ var order = Order.Create(TimeProvider.System);
+
+ var result = order.Ship(TimeProvider.System);
+
+ Assert.Equal("Order.Empty", result.Error!.Code);
+ }
+
+ [Fact]
+ public void Ship_WhenPaidInFull_UsesTheClockForTheShippingDate()
+ {
+ var now = new DateTimeOffset(2026, 10, 1, 9, 30, 0, TimeSpan.Zero);
+ var order = NewOrder();
+ order.AddPayment(order.Total);
+
+ var result = order.Ship(new FixedClock(now));
+
+ Assert.True(result.IsSuccess);
+ Assert.Equal(OrderStatus.InTransit, order.Status);
+ Assert.Equal(now.UtcDateTime, order.ShippedAtUtc);
+ }
+
+ [Fact]
+ public void Ship_Twice_ReturnsAlreadyShipped()
+ {
+ var order = NewOrder();
+ order.AddPayment(order.Total);
+ order.Ship(TimeProvider.System);
+
+ var result = order.Ship(TimeProvider.System);
+
+ Assert.Equal("Order.AlreadyShipped", result.Error!.Code);
+ }
+}
+
+internal sealed class FixedClock(DateTimeOffset now) : TimeProvider
+{
+ public override DateTimeOffset GetUtcNow() => now;
+}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign.sln b/csharp-design-patterns/AggregateDesign/AggregateDesign.sln
index 383d4f2071..acb785f89f 100644
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign.sln
+++ b/csharp-design-patterns/AggregateDesign/AggregateDesign.sln
@@ -1,31 +1,62 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.4.33110.190
+VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AggregateDesign", "AggregateDesign\AggregateDesign.csproj", "{38B0CE87-E1B9-4280-BE77-91853D104CE9}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AggregateDesign.Domain", "AggregateDesign.Domain\AggregateDesign.Domain.csproj", "{31B0BA36-6994-498B-890E-A24A3AC0DBDD}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{CCCE3132-ACA0-4261-9742-1E0F847B906B}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AggregateDesign.Infrastructure", "AggregateDesign.Infrastructure\AggregateDesign.Infrastructure.csproj", "{50C11229-31C6-4CF0-BAB2-B0512107880C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AggregateDesign.Tests", "AggregateDesign.Tests\AggregateDesign.Tests.csproj", "{356C4F08-072A-41B5-BE32-F3D72275D2F5}"
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
- {38B0CE87-E1B9-4280-BE77-91853D104CE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {38B0CE87-E1B9-4280-BE77-91853D104CE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {38B0CE87-E1B9-4280-BE77-91853D104CE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {38B0CE87-E1B9-4280-BE77-91853D104CE9}.Release|Any CPU.Build.0 = Release|Any CPU
- {CCCE3132-ACA0-4261-9742-1E0F847B906B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CCCE3132-ACA0-4261-9742-1E0F847B906B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CCCE3132-ACA0-4261-9742-1E0F847B906B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CCCE3132-ACA0-4261-9742-1E0F847B906B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {31B0BA36-6994-498B-890E-A24A3AC0DBDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {31B0BA36-6994-498B-890E-A24A3AC0DBDD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {31B0BA36-6994-498B-890E-A24A3AC0DBDD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {31B0BA36-6994-498B-890E-A24A3AC0DBDD}.Debug|x64.Build.0 = Debug|Any CPU
+ {31B0BA36-6994-498B-890E-A24A3AC0DBDD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {31B0BA36-6994-498B-890E-A24A3AC0DBDD}.Debug|x86.Build.0 = Debug|Any CPU
+ {31B0BA36-6994-498B-890E-A24A3AC0DBDD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {31B0BA36-6994-498B-890E-A24A3AC0DBDD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {31B0BA36-6994-498B-890E-A24A3AC0DBDD}.Release|x64.ActiveCfg = Release|Any CPU
+ {31B0BA36-6994-498B-890E-A24A3AC0DBDD}.Release|x64.Build.0 = Release|Any CPU
+ {31B0BA36-6994-498B-890E-A24A3AC0DBDD}.Release|x86.ActiveCfg = Release|Any CPU
+ {31B0BA36-6994-498B-890E-A24A3AC0DBDD}.Release|x86.Build.0 = Release|Any CPU
+ {50C11229-31C6-4CF0-BAB2-B0512107880C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {50C11229-31C6-4CF0-BAB2-B0512107880C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {50C11229-31C6-4CF0-BAB2-B0512107880C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {50C11229-31C6-4CF0-BAB2-B0512107880C}.Debug|x64.Build.0 = Debug|Any CPU
+ {50C11229-31C6-4CF0-BAB2-B0512107880C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {50C11229-31C6-4CF0-BAB2-B0512107880C}.Debug|x86.Build.0 = Debug|Any CPU
+ {50C11229-31C6-4CF0-BAB2-B0512107880C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {50C11229-31C6-4CF0-BAB2-B0512107880C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {50C11229-31C6-4CF0-BAB2-B0512107880C}.Release|x64.ActiveCfg = Release|Any CPU
+ {50C11229-31C6-4CF0-BAB2-B0512107880C}.Release|x64.Build.0 = Release|Any CPU
+ {50C11229-31C6-4CF0-BAB2-B0512107880C}.Release|x86.ActiveCfg = Release|Any CPU
+ {50C11229-31C6-4CF0-BAB2-B0512107880C}.Release|x86.Build.0 = Release|Any CPU
+ {356C4F08-072A-41B5-BE32-F3D72275D2F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {356C4F08-072A-41B5-BE32-F3D72275D2F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {356C4F08-072A-41B5-BE32-F3D72275D2F5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {356C4F08-072A-41B5-BE32-F3D72275D2F5}.Debug|x64.Build.0 = Debug|Any CPU
+ {356C4F08-072A-41B5-BE32-F3D72275D2F5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {356C4F08-072A-41B5-BE32-F3D72275D2F5}.Debug|x86.Build.0 = Debug|Any CPU
+ {356C4F08-072A-41B5-BE32-F3D72275D2F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {356C4F08-072A-41B5-BE32-F3D72275D2F5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {356C4F08-072A-41B5-BE32-F3D72275D2F5}.Release|x64.ActiveCfg = Release|Any CPU
+ {356C4F08-072A-41B5-BE32-F3D72275D2F5}.Release|x64.Build.0 = Release|Any CPU
+ {356C4F08-072A-41B5-BE32-F3D72275D2F5}.Release|x86.ActiveCfg = Release|Any CPU
+ {356C4F08-072A-41B5-BE32-F3D72275D2F5}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {70D1BE9F-4277-4D30-9B54-E24D8BB5F857}
- EndGlobalSection
EndGlobal
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/AggregateDesign.csproj b/csharp-design-patterns/AggregateDesign/AggregateDesign/AggregateDesign.csproj
deleted file mode 100644
index 4ffb5efc44..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/AggregateDesign.csproj
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
- net7.0
- enable
- enable
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrderItemModel.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrderItemModel.cs
deleted file mode 100644
index 2cf58facb1..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrderItemModel.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace AggregateDesign.DataAccess;
-
-[Table("OrderItem")]
-public class OrderItemModel
-{
- [Key]
- public long OrderItemId { get; set; }
-
- public long OrderId { get; set; }
-
- public OrderModel? Order { get; set; }
-
- public string? ItemName { get; set; }
-
- public uint Quantity { get; set; }
-
- public decimal UnitPrice { get; set; }
-}
\ No newline at end of file
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrderModel.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrderModel.cs
deleted file mode 100644
index 422f44c605..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrderModel.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace AggregateDesign.DataAccess;
-
-[Table("Orders")]
-public class OrderModel
-{
- [Key]
- public long OrderId { get; set; }
-
- public DateTime CreationDate { get; set; }
-
- public decimal PaidAmount { get; set; }
-
- public DateTime? ShippingDate { get; set; }
-
- public OrderModelStatus Status { get; set; }
-
- public ICollection Items { get; set; } = new List();
-}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrderModelStatus.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrderModelStatus.cs
deleted file mode 100644
index 9a542ff57c..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrderModelStatus.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-namespace AggregateDesign.DataAccess;
-
-public enum OrderModelStatus { PendingPayment, ReadyForShipping, InTransit }
\ No newline at end of file
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrdersDbContext.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrdersDbContext.cs
deleted file mode 100644
index 2be986e04f..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrdersDbContext.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-
-namespace AggregateDesign.DataAccess;
-
-#nullable disable
-
-public class OrdersDbContext : DbContext
-{
- public DbSet Orders { get; set; }
- public DbSet OrderItems { get; set; }
-
- protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
- {
- optionsBuilder.UseNpgsql("Host=localhost;Database=orders;Username=postgres;Password=a");
- }
-}
\ No newline at end of file
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrdersRepository.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrdersRepository.cs
deleted file mode 100644
index 5349f8b65f..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/DataAccess/OrdersRepository.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using AggregateDesign.Domain;
-using AutoMapper;
-using Microsoft.EntityFrameworkCore;
-
-namespace AggregateDesign.DataAccess;
-
-public class OrdersRepository : IOrdersRepository
-{
- private readonly OrdersDbContext _dbContext;
- private readonly IMapper _mapper;
-
- public OrdersRepository(OrdersDbContext dbContext, IMapper mapper)
- {
- _dbContext = dbContext;
- _mapper = mapper;
- }
-
- public async Task GetByIdAsync(long id)
- {
- var dataModel = await _dbContext.Orders
- .Include(x => x.Items)
- .FirstAsync(x => x.OrderId == id);
-
- if (dataModel is null)
- return null;
-
- var entity = _mapper.Map(dataModel);
- return entity;
- }
-
- public async Task CreateAsync(Order entity)
- {
- var dataModel = _mapper.Map(entity);
- var entry = _dbContext.Add(dataModel);
- await _dbContext.SaveChangesAsync();
-
- return _mapper.Map(entry.Entity);
- }
-
- public async Task UpdateAsync(Order entity)
- {
- var dataModel = _mapper.Map(entity);
- var attached = await _dbContext.Orders
- .Include(x => x.Items)
- .SingleAsync(x => x.OrderId == entity.OrderId);
-
- _dbContext.Entry(attached).State = EntityState.Detached;
- foreach (var item in attached.Items.ToList())
- _dbContext.Entry(item).State = EntityState.Detached;
-
- var entry = _dbContext.Attach(dataModel);
-
- await _dbContext.SaveChangesAsync();
-
- return _mapper.Map(entry.Entity);
- }
-}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/Domain/IOrdersRepository.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/Domain/IOrdersRepository.cs
deleted file mode 100644
index fcbc7dd87d..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/Domain/IOrdersRepository.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace AggregateDesign.Domain;
-
-public interface IOrdersRepository
-{
- Task GetByIdAsync(long id);
- Task CreateAsync(Order entity);
- Task UpdateAsync(Order entity);
-}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/Domain/Order.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/Domain/Order.cs
deleted file mode 100644
index eba306487e..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/Domain/Order.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using System.Collections.ObjectModel;
-
-namespace AggregateDesign.Domain;
-
-public class Order
-{
- internal List _items = new();
-
- public long OrderId { get; private set; }
-
- public DateTime CreationDate { get; private set; }
-
- public decimal PaidAmount { get; private set; }
-
- public DateTime? ShippingDate { get; private set; }
-
- public OrderStatus Status { get; private set; }
-
- public Order()
- {
- Status= OrderStatus.PendingPayment;
- CreationDate= DateTime.UtcNow;
- }
-
- public IReadOnlyCollection Items => new ReadOnlyCollection(_items);
-
- public decimal OrderTotal => _items.Sum(x => Convert.ToDecimal(x.Quantity) * x.UnitPrice);
-
- public void AddPayment(decimal amount)
- {
- if (amount <= 0)
- throw new InvalidOperationException("Amount must be positive.");
-
- if (amount > OrderTotal - PaidAmount)
- throw new InvalidOperationException("Payment can't exceed order total");
-
- PaidAmount += amount;
- if (PaidAmount >= OrderTotal)
- Status= OrderStatus.ReadyForShipping;
- }
-
- public void AddItem(string itemName, uint quantity, decimal unitPrice)
- {
- if (Status != OrderStatus.PendingPayment)
- throw new InvalidOperationException("Can't modify order once payment has been done.");
-
- _items.Add(new OrderItem(itemName, quantity, unitPrice));
- }
-
- public void RemoveItem(string itemName)
- {
- if (Status != OrderStatus.PendingPayment)
- throw new InvalidOperationException("Can't modify order once payment has been done.");
-
- _items.RemoveAll(x => x.ItemName == itemName);
- }
-
- public void AddQuantity(string itemName, uint quantity)
- => _items.FirstOrDefault(x => x.ItemName.Equals(itemName))?.AddQuantity(quantity);
-
- public void WithdrawQuantity(string itemName, uint quantity)
- => _items.FirstOrDefault(x => x.ItemName.Equals(itemName))?.WithdrawQuantity(quantity);
-
- public void ShipOrder()
- {
- if (_items.Sum(x => x.Quantity) <= 0)
- throw new InvalidOperationException("Can´t ship an order with no items.");
-
- if (Status == OrderStatus.PendingPayment)
- throw new InvalidOperationException("Can´t ship order unpaid order.");
-
- if (Status == OrderStatus.InTransit)
- throw new InvalidOperationException("Order already shipped to customer.");
-
- ShippingDate = DateTime.Now;
- Status = OrderStatus.InTransit;
- }
-}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/Domain/OrderItem.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/Domain/OrderItem.cs
deleted file mode 100644
index 14f2a0b239..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/Domain/OrderItem.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-namespace AggregateDesign.Domain;
-
-public class OrderItem
-{
- public long OrderItemId { get; private set; }
-
- public string ItemName { get; private set; }
-
- public uint Quantity { get; private set; }
-
- public decimal UnitPrice { get; private set; }
-
- private OrderItem() {}
-
- internal OrderItem(string itemName, uint quantity, decimal unitPrice)
- {
- if (string.IsNullOrEmpty(itemName))
- throw new ArgumentException($"'{nameof(itemName)}' cannot be null or empty.", nameof(itemName));
-
- if (quantity == 0)
- throw new ArgumentException("Quantity must be at least one.", nameof(quantity));
-
- if (unitPrice <= 0)
- throw new ArgumentException("Unit price must be above zero.", nameof(unitPrice));
-
- ItemName = itemName;
- Quantity = quantity;
- UnitPrice = unitPrice;
- }
-
- internal void AddQuantity(uint quantity)
- {
- this.Quantity += quantity;
- }
-
- internal void WithdrawQuantity(uint quantity)
- {
- if (this.Quantity - quantity <= 0)
- throw new InvalidOperationException("Can't remove all units from an order item. Remove the entire order item instead.");
-
- this.Quantity -= quantity;
- }
-}
\ No newline at end of file
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/Domain/OrderStatus.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/Domain/OrderStatus.cs
deleted file mode 100644
index 306e38576e..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/Domain/OrderStatus.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-namespace AggregateDesign.Domain;
-
-public enum OrderStatus { PendingPayment, ReadyForShipping, InTransit }
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/Mapping/OrderMappingProfile.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/Mapping/OrderMappingProfile.cs
deleted file mode 100644
index 3beb489630..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/Mapping/OrderMappingProfile.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using AggregateDesign.DataAccess;
-using AggregateDesign.Domain;
-using AutoMapper;
-
-namespace AggregateDesign.Mapping
-{
- public class OrderMappingProfile : Profile
- {
- public OrderMappingProfile()
- {
- CreateMap();
-
- CreateMap()
- .ForMember(dest => dest.Items, opt => opt.Ignore())
- .ForMember(dest => dest._items, opt => opt.MapFrom(src => src.Items));
-
- CreateMap()
- .ReverseMap();
- }
- }
-}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/Migrations/20230319144626_InitialDatabaseCreation.Designer.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/Migrations/20230319144626_InitialDatabaseCreation.Designer.cs
deleted file mode 100644
index c4ad108ef5..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/Migrations/20230319144626_InitialDatabaseCreation.Designer.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-//
-using System;
-using AggregateDesign.DataAccess;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
-
-#nullable disable
-
-namespace AggregateDesign.Migrations
-{
- [DbContext(typeof(OrdersDbContext))]
- [Migration("20230319144626_InitialDatabaseCreation")]
- partial class InitialDatabaseCreation
- {
- ///
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "7.0.3")
- .HasAnnotation("Relational:MaxIdentifierLength", 63);
-
- NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
-
- modelBuilder.Entity("AggregateDesign.DataAccess.OrderItemModel", b =>
- {
- b.Property("OrderItemId")
- .ValueGeneratedOnAdd()
- .HasColumnType("bigint");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("OrderItemId"));
-
- b.Property("ItemName")
- .IsRequired()
- .HasColumnType("text");
-
- b.Property("OrderId")
- .HasColumnType("bigint");
-
- b.Property("Quantity")
- .HasColumnType("bigint");
-
- b.Property("UnitPrice")
- .HasColumnType("numeric");
-
- b.HasKey("OrderItemId");
-
- b.HasIndex("OrderId");
-
- b.ToTable("OrderItem");
- });
-
- modelBuilder.Entity("AggregateDesign.DataAccess.OrderModel", b =>
- {
- b.Property("OrderId")
- .ValueGeneratedOnAdd()
- .HasColumnType("bigint");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("OrderId"));
-
- b.Property("CreationDate")
- .HasColumnType("timestamp with time zone");
-
- b.Property("PaidAmount")
- .HasColumnType("numeric");
-
- b.Property("ShippingDate")
- .HasColumnType("timestamp with time zone");
-
- b.Property("Status")
- .HasColumnType("integer");
-
- b.HasKey("OrderId");
-
- b.ToTable("Orders");
- });
-
- modelBuilder.Entity("AggregateDesign.DataAccess.OrderItemModel", b =>
- {
- b.HasOne("AggregateDesign.DataAccess.OrderModel", "Order")
- .WithMany("Items")
- .HasForeignKey("OrderId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Order");
- });
-
- modelBuilder.Entity("AggregateDesign.DataAccess.OrderModel", b =>
- {
- b.Navigation("Items");
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/Migrations/20230319144626_InitialDatabaseCreation.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/Migrations/20230319144626_InitialDatabaseCreation.cs
deleted file mode 100644
index 67008814c8..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/Migrations/20230319144626_InitialDatabaseCreation.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
-
-#nullable disable
-
-namespace AggregateDesign.Migrations
-{
- ///
- public partial class InitialDatabaseCreation : Migration
- {
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "Orders",
- columns: table => new
- {
- OrderId = table.Column(type: "bigint", nullable: false)
- .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
- CreationDate = table.Column(type: "timestamp with time zone", nullable: false),
- PaidAmount = table.Column(type: "numeric", nullable: false),
- ShippingDate = table.Column(type: "timestamp with time zone", nullable: true),
- Status = table.Column(type: "integer", nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_Orders", x => x.OrderId);
- });
-
- migrationBuilder.CreateTable(
- name: "OrderItem",
- columns: table => new
- {
- OrderItemId = table.Column(type: "bigint", nullable: false)
- .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
- OrderId = table.Column(type: "bigint", nullable: false),
- ItemName = table.Column(type: "text", nullable: false),
- Quantity = table.Column(type: "bigint", nullable: false),
- UnitPrice = table.Column(type: "numeric", nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_OrderItem", x => x.OrderItemId);
- table.ForeignKey(
- name: "FK_OrderItem_Orders_OrderId",
- column: x => x.OrderId,
- principalTable: "Orders",
- principalColumn: "OrderId",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateIndex(
- name: "IX_OrderItem_OrderId",
- table: "OrderItem",
- column: "OrderId");
- }
-
- ///
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "OrderItem");
-
- migrationBuilder.DropTable(
- name: "Orders");
- }
- }
-}
diff --git a/csharp-design-patterns/AggregateDesign/AggregateDesign/Migrations/OrdersDbContextModelSnapshot.cs b/csharp-design-patterns/AggregateDesign/AggregateDesign/Migrations/OrdersDbContextModelSnapshot.cs
deleted file mode 100644
index 07c6749c95..0000000000
--- a/csharp-design-patterns/AggregateDesign/AggregateDesign/Migrations/OrdersDbContextModelSnapshot.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-//
-using System;
-using AggregateDesign.DataAccess;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
-
-#nullable disable
-
-namespace AggregateDesign.Migrations
-{
- [DbContext(typeof(OrdersDbContext))]
- partial class OrdersDbContextModelSnapshot : ModelSnapshot
- {
- protected override void BuildModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "7.0.3")
- .HasAnnotation("Relational:MaxIdentifierLength", 63);
-
- NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
-
- modelBuilder.Entity("AggregateDesign.DataAccess.OrderItemModel", b =>
- {
- b.Property("OrderItemId")
- .ValueGeneratedOnAdd()
- .HasColumnType("bigint");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("OrderItemId"));
-
- b.Property("ItemName")
- .IsRequired()
- .HasColumnType("text");
-
- b.Property("OrderId")
- .HasColumnType("bigint");
-
- b.Property("Quantity")
- .HasColumnType("bigint");
-
- b.Property("UnitPrice")
- .HasColumnType("numeric");
-
- b.HasKey("OrderItemId");
-
- b.HasIndex("OrderId");
-
- b.ToTable("OrderItem");
- });
-
- modelBuilder.Entity("AggregateDesign.DataAccess.OrderModel", b =>
- {
- b.Property("OrderId")
- .ValueGeneratedOnAdd()
- .HasColumnType("bigint");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("OrderId"));
-
- b.Property("CreationDate")
- .HasColumnType("timestamp with time zone");
-
- b.Property("PaidAmount")
- .HasColumnType("numeric");
-
- b.Property("ShippingDate")
- .HasColumnType("timestamp with time zone");
-
- b.Property("Status")
- .HasColumnType("integer");
-
- b.HasKey("OrderId");
-
- b.ToTable("Orders");
- });
-
- modelBuilder.Entity("AggregateDesign.DataAccess.OrderItemModel", b =>
- {
- b.HasOne("AggregateDesign.DataAccess.OrderModel", "Order")
- .WithMany("Items")
- .HasForeignKey("OrderId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Order");
- });
-
- modelBuilder.Entity("AggregateDesign.DataAccess.OrderModel", b =>
- {
- b.Navigation("Items");
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/csharp-design-patterns/AggregateDesign/Test/ApplicationCollection.cs b/csharp-design-patterns/AggregateDesign/Test/ApplicationCollection.cs
deleted file mode 100644
index 42404104b7..0000000000
--- a/csharp-design-patterns/AggregateDesign/Test/ApplicationCollection.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Test;
-
-[CollectionDefinition("Application collection")]
-public class ApplicationCollection : ICollectionFixture
-{
- // This class has no code, and is never created. Its purpose is simply
- // to be the place to apply [CollectionDefinition] and all the
- // ICollectionFixture<> interfaces.
-}
\ No newline at end of file
diff --git a/csharp-design-patterns/AggregateDesign/Test/ApplicationFixture.cs b/csharp-design-patterns/AggregateDesign/Test/ApplicationFixture.cs
deleted file mode 100644
index 50b7bcfdde..0000000000
--- a/csharp-design-patterns/AggregateDesign/Test/ApplicationFixture.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using AggregateDesign.DataAccess;
-using AggregateDesign.Domain;
-using AggregateDesign.Mapping;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Test
-{
- public class ApplicationFixture : IDisposable
- {
- public ServiceProvider ServiceProvider { get; }
-
- public ApplicationFixture()
- {
- ServiceProvider = new ServiceCollection()
- .AddAutoMapper(typeof(OrderMappingProfile))
- .AddDbContext()
- .AddTransient()
- .BuildServiceProvider();
- }
-
- public void Dispose()
- {
- ServiceProvider.Dispose();
- }
- }
-}
diff --git a/csharp-design-patterns/AggregateDesign/Test/OrderAggregateLiveTest.cs b/csharp-design-patterns/AggregateDesign/Test/OrderAggregateLiveTest.cs
deleted file mode 100644
index f757c935d7..0000000000
--- a/csharp-design-patterns/AggregateDesign/Test/OrderAggregateLiveTest.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using AggregateDesign.DataAccess;
-using AggregateDesign.Domain;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Test
-{
- [Collection("Application collection")]
- public class OrderAggregateLiveTest
- {
- private ApplicationFixture _fixture;
-
- public OrderAggregateLiveTest(ApplicationFixture fixture )
- {
- _fixture = fixture;
- }
-
- [Fact]
- public async Task WhenCreatedOrderIsSaved_ThenIsWrittenToDatabase()
- {
- IOrdersRepository ordersRepository = _fixture.ServiceProvider.GetRequiredService();
- Order order = new();
-
- order.AddItem("Cloudsoft Women's Running Shoes", 1, 59.99M);
- order.AddItem("Gildone Men's Crew T-Shirt", 3, 18.99M);
- order.AddPayment(100.0M);
-
- var created = await ordersRepository.CreateAsync(order);
- }
-
- [Fact]
- public async Task WhenReadOrderIsUpdated_ThenUpdatesAreWritten()
- {
- IOrdersRepository ordersRepository = _fixture.ServiceProvider.GetRequiredService();
-
- // Create an order.
- Order order = new();
- order.AddItem("Enameled Cast Iron Covered Dutch Oven", 1, 76.35M);
- order.AddItem("Skillet with Red Silicone Hot Handle Holder, 12-inch", 1, 29.90M);
- var created = await ordersRepository.CreateAsync(order);
-
- // Read the created order from database and update it.
- Order? readOrder = await ordersRepository.GetByIdAsync(created.OrderId);
- Assert.NotNull(readOrder);
-
- readOrder.AddQuantity("Skillet with Red Silicone Hot Handle Holder, 12-inch", 1);
- readOrder.AddPayment(100.0M);
- await ordersRepository.UpdateAsync(readOrder);
-
- // Read the updated order and assert the changes.
- var updatedOrder = await ordersRepository.GetByIdAsync(readOrder.OrderId);
- Assert.NotNull(updatedOrder);
- Assert.Equal(100.0M, updatedOrder?.PaidAmount);
- Assert.Equal(2, (int?)updatedOrder?.Items?.FirstOrDefault(x => x.ItemName == "Skillet with Red Silicone Hot Handle Holder, 12-inch")?.Quantity);
- }
-
- [Fact]
- public async Task WhenReadOrderIsRetrieved_ThenSameDataIsRead()
- {
- IOrdersRepository ordersRepository = _fixture.ServiceProvider.GetRequiredService();
-
- // Create an order.
- Order order = new();
- order.AddItem("Enameled Cast Iron Covered Dutch Oven", 1, 76.35M);
- order.AddItem("Skillet with Red Silicone Hot Handle Holder, 12-inch", 1, 29.90M);
- var created = await ordersRepository.CreateAsync(order);
-
- // Read the created order from database.
- Order? readOrder = await ordersRepository.GetByIdAsync(created.OrderId);
- Assert.NotNull(readOrder);
- Assert.Equal(2, readOrder.Items.Count);
- Assert.Collection(readOrder.Items,
- item => Assert.Equal("Enameled Cast Iron Covered Dutch Oven", item.ItemName),
- item => Assert.Equal("Skillet with Red Silicone Hot Handle Holder, 12-inch", item.ItemName));
- }
- }
-}
\ No newline at end of file
diff --git a/csharp-design-patterns/AggregateDesign/Test/OrderAggregateUnitTest.cs b/csharp-design-patterns/AggregateDesign/Test/OrderAggregateUnitTest.cs
deleted file mode 100644
index 7bbad083c5..0000000000
--- a/csharp-design-patterns/AggregateDesign/Test/OrderAggregateUnitTest.cs
+++ /dev/null
@@ -1,196 +0,0 @@
-using AggregateDesign.Domain;
-
-namespace Test
-{
- public class OrderAggregateUnitTest
- {
- [Fact]
- public void WhenOrderContainsItems_ThenOrderTotalReturnsPrice()
- {
- Order order = new();
- order.AddItem("Cloudsoft Women's Running Shoes", 2, 59.99M);
-
- Assert.Equal(119.98M, order.OrderTotal);
- }
-
- [Fact]
- public void WhenAddingOrderPayment_ThenPaidAmountIncreases()
- {
- Order order = new();
- order.AddItem("Cloudsoft Women's Running Shoes", 2, 59.99M);
- order.AddPayment(30.0M);
-
- Assert.Equal(30.0M, order.PaidAmount);
- }
-
- [Fact]
- public void WhenAddingOrderPayment_ThenCantAddNegativePayments()
- {
- Order order = new();
- order.AddItem("Cloudsoft Women's Running Shoes", 2, 59.99M);
- var exception = Assert.Throws(() =>
- {
- order.AddPayment(-30.0M);
- });
-
- Assert.Equal("Amount must be positive.", exception.Message);
- }
-
- [Fact]
- public void WhenAddingOrderPayment_ThenPaymentCantExceedOrderTotal()
- {
- Order order = new();
- order.AddItem("Cloudsoft Women's Running Shoes", 1, 59.99M);
- var exception = Assert.Throws(() =>
- {
- order.AddPayment(200.0M);
- });
-
- Assert.Equal("Payment can't exceed order total", exception.Message);
- }
-
- [Fact]
- public void WhenAddingOrderItem_ThenCantAddItemAfterPaymentMade()
- {
- Order order = new();
- order.AddItem("Cloudsoft Women's Running Shoes", 1, 50.00M);
- order.AddPayment(50.0M);
-
- var exception = Assert.Throws(() =>
- {
- order.AddItem("Gildone Men's Crew T-Shirt", 1, 10.00M);
- });
-
- Assert.Equal("Can't modify order once payment has been done.", exception.Message);
- }
-
- [Fact]
- public void WhenRemovingOrderItem_ThenCantRemoveItemAfterPaymentMade()
- {
- Order order = new();
- order.AddItem("Cloudsoft Women's Running Shoes", 1, 50.00M);
- order.AddPayment(50.0M);
-
- var exception = Assert.Throws(() =>
- {
- order.RemoveItem("Cloudsoft Women's Running Shoes");
- });
-
- Assert.Equal("Can't modify order once payment has been done.", exception.Message);
- }
-
- [Fact]
- public void WhenShippingOrder_ThenCantShipEmptyOrder()
- {
- Order order = new();
- var exception = Assert.Throws(() =>
- {
- order.ShipOrder();
- });
-
- Assert.Equal("Can´t ship an order with no items.", exception.Message);
- }
-
- [Fact]
- public void WhenShippingOrder_ThenCantShipUnpaidOrder()
- {
- Order order = new();
- order.AddItem("Cloudsoft Women's Running Shoes", 1, 59.99M);
- order.AddItem("Gildone Men's Crew T-Shirt", 3, 18.99M);
- order.AddPayment(20.0M);
-
- var exception = Assert.Throws(() =>
- {
- order.ShipOrder();
- });
-
- Assert.Equal("Can´t ship order unpaid order.", exception.Message);
- }
-
- [Fact]
- public void WhenShippingOrder_ThenCantShipAlreadyShippedOrder()
- {
- Order order = new();
- order.AddItem("Cloudsoft Women's Running Shoes", 1, 59.99M);
- order.AddItem("Gildone Men's Crew T-Shirt", 3, 18.99M);
- order.AddPayment(order.OrderTotal);
- order.ShipOrder();
-
- var exception = Assert.Throws(() =>
- {
- order.ShipOrder();
- });
-
- Assert.Equal("Order already shipped to customer.", exception.Message);
- }
-
- [Fact]
- public void WhenAddingItems_ThenCantAddItemWithoutName()
- {
- Order order = new();
- var exception = Assert.Throws(() =>
- {
- order.AddItem("", 1, 59.99M);
- });
-
- Assert.Equal("'itemName' cannot be null or empty. (Parameter 'itemName')", exception.Message);
- }
-
- [Fact]
- public void WhenAddingItems_ThenCantAddItemWithZeroQuantity()
- {
- Order order = new();
- var exception = Assert.Throws(() =>
- {
- order.AddItem("Cloudsoft Women's Running Shoes", 0, 59.99M);
- });
-
- Assert.Equal("Quantity must be at least one. (Parameter 'quantity')", exception.Message);
- }
-
- [Fact]
- public void WhenAddingItems_ThenCantAddItemWithZeroUnitPrice()
- {
- Order order = new();
- var exception = Assert.Throws(() =>
- {
- order.AddItem("Cloudsoft Women's Running Shoes", 1, .0M);
- });
-
- Assert.Equal("Unit price must be above zero. (Parameter 'unitPrice')", exception.Message);
- }
-
- [Fact]
- public void WhenAddingItemQuantity_ThenQuantityIsAdded()
- {
- Order order = new();
- order.AddItem("Cloudsoft Women's Running Shoes", 1, 19.99M);
- order.AddQuantity("Cloudsoft Women's Running Shoes", 10);
-
- Assert.Equal((int?)11, (int?)order.Items.FirstOrDefault(x => x.ItemName.Equals("Cloudsoft Women's Running Shoes"))?.Quantity);
- }
-
- [Fact]
- public void WhenWithdrawingItemQuantity_ThenQuantityIsWithdrawn()
- {
- Order order = new();
- order.AddItem("Cloudsoft Women's Running Shoes", 10, 19.99M);
- order.WithdrawQuantity("Cloudsoft Women's Running Shoes", 5);
-
- Assert.Equal((int?)5, (int?)order.Items.FirstOrDefault(x => x.ItemName.Equals("Cloudsoft Women's Running Shoes"))?.Quantity);
- }
-
- [Fact]
- public void WhenWithdrawingItemQuantity_ThenCantWithdrawAllUnits()
- {
- Order order = new();
- order.AddItem("Cloudsoft Women's Running Shoes", 10, 19.99M);
- var exception = Assert.Throws(() =>
- {
- order.WithdrawQuantity("Cloudsoft Women's Running Shoes", 10);
- });
-
- Assert.Equal("Can't remove all units from an order item. Remove the entire order item instead.", exception.Message);
- }
- }
-}
diff --git a/csharp-design-patterns/AggregateDesign/Test/Test.csproj b/csharp-design-patterns/AggregateDesign/Test/Test.csproj
deleted file mode 100644
index e023347f8e..0000000000
--- a/csharp-design-patterns/AggregateDesign/Test/Test.csproj
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
- net7.0
- enable
- enable
-
- false
-
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
-
-
-
diff --git a/csharp-design-patterns/AggregateDesign/Test/Usings.cs b/csharp-design-patterns/AggregateDesign/Test/Usings.cs
deleted file mode 100644
index 8c927eb747..0000000000
--- a/csharp-design-patterns/AggregateDesign/Test/Usings.cs
+++ /dev/null
@@ -1 +0,0 @@
-global using Xunit;
\ No newline at end of file