diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/Common.csproj b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/Common.csproj
deleted file mode 100644
index 9e0bc4931a..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/Common.csproj
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- net8.0
- enable
- enable
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/IRabbitMqConnectionManager.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/IRabbitMqConnectionManager.cs
deleted file mode 100644
index a7c2f219f1..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/IRabbitMqConnectionManager.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using RabbitMQ.Client;
-
-namespace Common.RabbitMq;
-
-public interface IRabbitMqConnectionManager
-{
- IModel? Channel { get; }
- IConnection? Connection { get; }
-
- void InitializeConnection();
-}
\ No newline at end of file
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/RabbitMqConfiguration.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/RabbitMqConfiguration.cs
deleted file mode 100644
index 14c9d25490..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/RabbitMqConfiguration.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Common.RabbitMq;
-
-public class RabbitMqConfiguration
-{
- public required string HostName { get; set; }
- public int Port { get; set; }
- public required string UserName { get; set; }
- public required string Password { get; set; }
- public required string QueueName { get; set; }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/RabbitMqConnectionManager.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/RabbitMqConnectionManager.cs
deleted file mode 100644
index 358e219120..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/RabbitMqConnectionManager.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Options;
-using RabbitMQ.Client;
-
-namespace Common.RabbitMq;
-
-internal class RabbitMqConnectionManager(IOptions rabbitMqConfiguration)
- : IDisposable, IRabbitMqConnectionManager
-{
- public IConnection? Connection { get; private set; }
- public IModel? Channel { get; private set; }
-
- public void InitializeConnection()
- {
- var connectionFactory = new ConnectionFactory
- {
- HostName = rabbitMqConfiguration.Value.HostName,
- Port = rabbitMqConfiguration.Value.Port,
- UserName = rabbitMqConfiguration.Value.UserName,
- Password = rabbitMqConfiguration.Value.Password
- };
- Connection = connectionFactory.CreateConnection();
- Channel = Connection.CreateModel();
- }
-
- public void Dispose()
- {
- Channel?.Close();
- Connection?.Close();
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/ServiceCollectionExtensions.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/ServiceCollectionExtensions.cs
deleted file mode 100644
index f6aa7689f6..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Common.RabbitMq;
-
-public static class ServiceCollectionExtensions
-{
- public static void AddRabbitMqConnectionManager(this IServiceCollection services, IConfiguration configuration)
- {
- services.AddSingleton();
-
- services.Configure(configuration.GetSection(nameof(RabbitMqConfiguration)));
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/WebApplicationExtensions.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/WebApplicationExtensions.cs
deleted file mode 100644
index d9854bf53a..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Common/RabbitMq/WebApplicationExtensions.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Microsoft.AspNetCore.Builder;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Common.RabbitMq;
-
-public static class WebApplicationExtensions
-{
- public static void UseRabbitMqConnectionManager(this WebApplication app)
- {
- var rabbitMqConnectionManager = app.Services.GetRequiredService();
- rabbitMqConnectionManager.InitializeConnection();
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory.Models/ItemDto.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory.Models/ItemDto.cs
deleted file mode 100644
index b481acc1dc..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory.Models/ItemDto.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Inventory.Models;
-
-public class ItemDto
-{
- public Guid Id { get; set; }
- public required string Name { get; set; }
- public int Quantity { get; set; }
- public bool IsOutOfStock => Quantity == 0;
- public double Price { get; set; }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory.Models/UpdateQuantityDto.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory.Models/UpdateQuantityDto.cs
deleted file mode 100644
index c6dde4aaa4..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory.Models/UpdateQuantityDto.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Inventory.Models;
-
-public class UpdateQuantityDto
-{
- public Guid ItemId { get; set; }
- public int Amount { get; set; }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Controllers/ItemController.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Controllers/ItemController.cs
deleted file mode 100644
index 23ecc67441..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Controllers/ItemController.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using Inventory.Interfaces;
-using Microsoft.AspNetCore.Mvc;
-
-namespace Inventory.Controllers;
-
-[Route("api/[controller]")]
-[ApiController]
-public class ItemController(IItemService itemService) : ControllerBase
-{
- [HttpGet("{id:guid}")]
- public IActionResult Get(Guid id)
- {
- var item = itemService.Get(id);
-
- return item is null ?
- NotFound(id) :
- Ok(item);
- }
-
- [HttpGet]
- public IActionResult GetAll()
- {
- var items = itemService.GetAll();
-
- return Ok(items);
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Entities/Item.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Entities/Item.cs
deleted file mode 100644
index 1190d81b92..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Entities/Item.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Inventory.Models;
-
-namespace Inventory.Entities;
-
-internal class Item
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- public required string Name { get; set; }
- public int Quantity { get; set; }
- public double Price { get; set; }
-
- public ItemDto ToDto()
- {
- return new ItemDto
- {
- Id = Id,
- Name = Name,
- Quantity = Quantity,
- Price = Price
- };
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Interfaces/IItemRepository.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Interfaces/IItemRepository.cs
deleted file mode 100644
index b31cb2cf26..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Interfaces/IItemRepository.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Inventory.Models;
-
-namespace Inventory.Interfaces;
-
-internal interface IItemRepository
-{
- ItemDto? Get(Guid id);
- List GetAll();
- bool UpdateQuantity(UpdateQuantityDto updateQuantityDto);
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Interfaces/IItemService.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Interfaces/IItemService.cs
deleted file mode 100644
index 5eabef91c0..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Interfaces/IItemService.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Inventory.Models;
-
-namespace Inventory.Interfaces;
-
-public interface IItemService
-{
- ItemDto? Get(Guid id);
- List GetAll();
- bool UpdateQuantity(UpdateQuantityDto updateQuantityDto);
-}
\ No newline at end of file
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Interfaces/IRabbitMqConsumer.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Interfaces/IRabbitMqConsumer.cs
deleted file mode 100644
index e268645f48..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Interfaces/IRabbitMqConsumer.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Inventory.Interfaces;
-
-public interface IRabbitMqConsumer
-{
- void Consume();
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Inventory.csproj b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Inventory.csproj
deleted file mode 100644
index 4cf3fbbcb2..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Inventory.csproj
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- net8.0
- enable
- enable
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Properties/launchSettings.json b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Properties/launchSettings.json
deleted file mode 100644
index 28890d7eb9..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Properties/launchSettings.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "profiles": {
- "Inventory": {
- "commandName": "Project",
- "launchBrowser": true,
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- },
- "applicationUrl": "https://localhost:52178;http://localhost:52179"
- }
- }
-}
\ No newline at end of file
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Repositories/ItemRepository.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Repositories/ItemRepository.cs
deleted file mode 100644
index 13352ad365..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Repositories/ItemRepository.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using Inventory.Models;
-using Inventory.Entities;
-using Inventory.Interfaces;
-
-namespace Inventory.Repositories;
-
-internal class ItemRepository : IItemRepository
-{
- private static readonly List- _items =
- [
- new() { Id = Guid.Parse("111f9883-4b53-41eb-bcc3-8f4e6f29edf6"), Name = "Monitor", Quantity = 10, Price = 119.99 },
- new() { Id = Guid.Parse("bdabc506-3cac-47bf-b30b-a175e53cedfe"), Name = "Laptop", Quantity = 5, Price = 499.99 },
- new() { Id = Guid.Parse("e9386ab6-6a40-4fdf-876b-8efa7a3d30f0"), Name = "Keyboard", Quantity = 12, Price = 11.99 },
- new() { Id = Guid.Parse("14e92630-424a-4fd3-8657-4e56da9baf6b"), Name = "Mouse", Quantity = 15, Price = 7.99 }
- ];
-
- public ItemDto? Get(Guid id)
- {
- var item = _items.FirstOrDefault(x => x.Id == id);
-
- return item?.ToDto();
- }
-
- public List GetAll()
- {
- return _items.Select(x => x.ToDto()).ToList();
- }
-
- public bool UpdateQuantity(UpdateQuantityDto updateQuantityDto)
- {
- var item = _items.FirstOrDefault(i => i.Id == updateQuantityDto.ItemId);
-
- if (item is null) return false;
-
- item.Quantity += updateQuantityDto.Amount;
-
- return true;
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/ServiceCollectionExtensions.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/ServiceCollectionExtensions.cs
deleted file mode 100644
index 4a8ea884a3..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using Inventory.Interfaces;
-using Inventory.Repositories;
-using Inventory.Services;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Inventory;
-
-public static class ServiceCollectionExtensions
-{
- public static void AddInventoryModule(this IServiceCollection services)
- {
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Services/ItemService.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Services/ItemService.cs
deleted file mode 100644
index 5ce3c30dfe..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Services/ItemService.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Inventory.Interfaces;
-using Inventory.Models;
-
-namespace Inventory.Services;
-
-internal class ItemService(IItemRepository itemRepository) : IItemService
-{
- public ItemDto? Get(Guid id)
- {
- return itemRepository.Get(id);
- }
-
- public List GetAll()
- {
- return itemRepository.GetAll();
- }
-
- public bool UpdateQuantity(UpdateQuantityDto updateQuantityDto)
- {
- return itemRepository.UpdateQuantity(updateQuantityDto);
- }
-}
\ No newline at end of file
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Services/RabbitMqConsumer.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Services/RabbitMqConsumer.cs
deleted file mode 100644
index a3c30dbad2..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/Services/RabbitMqConsumer.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using Common.RabbitMq;
-using Inventory.Interfaces;
-using Inventory.Models;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
-using RabbitMQ.Client;
-using RabbitMQ.Client.Events;
-using System.Text;
-using System.Text.Json;
-
-namespace Inventory.Services;
-
-internal class RabbitMqConsumer(
- IServiceScopeFactory scopeFactory,
- IOptions rabbitMqConfiguration,
- IRabbitMqConnectionManager rabbitMqConnectionManager) : IRabbitMqConsumer
-{
- public void Consume()
- {
- var queueName = rabbitMqConfiguration.Value.QueueName;
- rabbitMqConnectionManager.Channel.QueueDeclare(
- queue: queueName,
- durable: true,
- exclusive: true,
- autoDelete: false);
-
- var consumer = new EventingBasicConsumer(rabbitMqConnectionManager.Channel);
-
- consumer.Received += HandleEventAsync;
-
- rabbitMqConnectionManager.Channel.BasicConsume(
- queue: queueName,
- autoAck: true,
- consumer: consumer);
- }
-
- private void HandleEventAsync(object? model, BasicDeliverEventArgs ea)
- {
- var body = ea.Body.ToArray();
- var message = Encoding.UTF8.GetString(body);
- var updateQuantityDto = JsonSerializer.Deserialize(message);
-
- if (updateQuantityDto is null)
- return;
-
- UpdateItemQuantity(updateQuantityDto);
- }
-
- private void UpdateItemQuantity(UpdateQuantityDto updateQuantityDto)
- {
- var scope = scopeFactory.CreateScope();
- var itemService = scope.ServiceProvider.GetRequiredService();
-
- itemService.UpdateQuantity(updateQuantityDto);
- }
-}
\ No newline at end of file
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/WebApplicationExtensions.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/WebApplicationExtensions.cs
deleted file mode 100644
index 9e753ffee9..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory/WebApplicationExtensions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Inventory.Interfaces;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Inventory;
-
-public static class WebApplicationExtensions
-{
- public static void UseOrderConsumer(this WebApplication app)
- {
- var orderConsumer = app.Services.GetRequiredService();
- orderConsumer.Consume();
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.Tests/IntergrationTests/InventoryModuleLiveTests.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.Tests/IntergrationTests/InventoryModuleLiveTests.cs
deleted file mode 100644
index c634aa5b18..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.Tests/IntergrationTests/InventoryModuleLiveTests.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using Inventory.Models;
-using Microsoft.AspNetCore.Mvc.Testing;
-using System.Net;
-using System.Net.Http.Json;
-
-
-namespace ModularMonolithPatternInCSharp.Tests.IntergrationTests;
-
-internal class InventoryModuleLiveTests : TestBase
-{
- [Test]
- public async Task WhenICallTheGetItemEndpoint_ThenItShouldReturnTheRequestedItem()
- {
- // Arrange
- var expected = new ItemDto() { Id = Guid.Parse("111f9883-4b53-41eb-bcc3-8f4e6f29edf6"), Name = "Monitor", Quantity = 10, Price = 119.99 };
-
- // Act
- var response = await _httpClient.GetAsync($"/api/Item/{expected.Id}");
-
- // Assert
- Assert.That(response, Is.Not.Null);
- Assert.DoesNotThrow(() => response.EnsureSuccessStatusCode());
- Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
-
- var actual = await response.Content.ReadFromJsonAsync();
-
- Assert.Multiple(() =>
- {
- Assert.That(actual, Is.Not.Null);
- Assert.That(expected.Id, Is.EqualTo(actual!.Id));
- Assert.That(expected.Name, Is.EqualTo(actual.Name));
- Assert.That(expected.Quantity, Is.EqualTo(actual.Quantity));
- Assert.That(expected.Price, Is.EqualTo(actual.Price));
- Assert.That(expected.IsOutOfStock, Is.EqualTo(actual.IsOutOfStock));
- });
- }
-
- [Test]
- public async Task WhenICallTheGetAllItemsEndpoint_ThenItShouldReturnAllItems()
- {
- // Arrange
- List expected =
- [
- new() { Id = Guid.Parse("111f9883-4b53-41eb-bcc3-8f4e6f29edf6"), Name = "Monitor", Quantity = 10, Price = 119.99 },
- new() { Id = Guid.Parse("bdabc506-3cac-47bf-b30b-a175e53cedfe"), Name = "Laptop", Quantity = 5, Price = 499.99 },
- new() { Id = Guid.Parse("e9386ab6-6a40-4fdf-876b-8efa7a3d30f0"), Name = "Keyboard", Quantity = 12, Price = 11.99 },
- new() { Id = Guid.Parse("14e92630-424a-4fd3-8657-4e56da9baf6b"), Name = "Mouse", Quantity = 15, Price = 7.99 }
- ];
-
- // Act
- var response = await _httpClient.GetAsync("/api/Item");
-
-
- // Assert
- Assert.That(response, Is.Not.Null);
- Assert.DoesNotThrow(() => response.EnsureSuccessStatusCode());
- Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
-
- var actual = await response.Content.ReadFromJsonAsync
>();
- Assert.That(actual, Is.Not.Null);
-
- foreach (var actaulItem in actual)
- {
- var expectedItemIndex = actual.IndexOf(actaulItem);
- var expectedItem = expected[expectedItemIndex];
- Assert.Multiple(() =>
- {
- Assert.That(actaulItem, Is.Not.Null);
- Assert.That(expectedItem.Id, Is.EqualTo(actaulItem!.Id));
- Assert.That(expectedItem.Name, Is.EqualTo(actaulItem.Name));
- Assert.That(expectedItem.Quantity, Is.EqualTo(actaulItem.Quantity));
- Assert.That(expectedItem.Price, Is.EqualTo(actaulItem.Price));
- Assert.That(expectedItem.IsOutOfStock, Is.EqualTo(actaulItem.IsOutOfStock));
- });
- }
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.Tests/IntergrationTests/OrderModuleLiveTests.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.Tests/IntergrationTests/OrderModuleLiveTests.cs
deleted file mode 100644
index 5630fc0155..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.Tests/IntergrationTests/OrderModuleLiveTests.cs
+++ /dev/null
@@ -1,124 +0,0 @@
-using Inventory.Models;
-using Order.Models;
-using Order.ViewModels;
-using System.Net;
-using System.Net.Http.Json;
-using System.Text;
-using System.Text.Json;
-
-namespace ModularMonolithPatternInCSharp.Tests.IntergrationTests;
-
-internal class OrderModuleLiveTests : TestBase
-{
- [Test, Order(1)]
- public async Task WhenICallTheAddOrderEndpoint_ThenItShouldReturnA200OkStatusCode()
- {
- // Arrange
- var orderViewModel = new OrderViewModel
- {
- Items =
- [
- new OrderItemViewModel() { ItemId = Guid.Parse("111f9883-4b53-41eb-bcc3-8f4e6f29edf6"), Quantity = 2 }
- ]
- };
- var content = new StringContent(JsonSerializer.Serialize(orderViewModel), Encoding.UTF8, "application/json");
-
- // Act
- var response = await _httpClient.PostAsync($"/api/Order", content);
-
- // Assert
- Assert.That(response, Is.Not.Null);
- Assert.DoesNotThrow(() => response.EnsureSuccessStatusCode());
- Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
- }
-
- [Test, Order(2)]
- public async Task GivenThatICreatedAnOrder_WhenIGetAllItems_ThenIShouldSeeTheNewOrder()
- {
- // Arrange
- var expected = new List
- {
- new()
- {
- Total = 239.98,
- Items =
- [
- new()
- {
- ItemId = Guid.Parse("111f9883-4b53-41eb-bcc3-8f4e6f29edf6"),
- ItemName = "Monitor",
- PricePerUnit = 119.99,
- Quantity = 2,
- Total = 239.98
- }
- ]
- }
- };
-
- // Act
- var response = await _httpClient.GetAsync("/api/Order");
-
- // Assert
- Assert.Multiple(() =>
- {
- Assert.That(response, Is.Not.Null);
- Assert.DoesNotThrow(() => response.EnsureSuccessStatusCode());
- });
-
- var actual = await response.Content.ReadFromJsonAsync>();
-
- Assert.Multiple(() =>
- {
- Assert.That(actual, Is.Not.Null);
- Assert.That(actual, Has.Count.EqualTo(expected.Count));
- });
-
- var expectedFirstOrder = expected[0];
- var actualFirstOrder = actual[0];
-
- Assert.Multiple(() =>
- {
- Assert.That(actualFirstOrder.Total, Is.EqualTo(expectedFirstOrder.Total));
- Assert.That(actualFirstOrder.Items, Has.Count.EqualTo(expectedFirstOrder.Items.Count));
- });
-
- var expectedFirstOrderItem = expectedFirstOrder.Items[0];
- var actualFirstOrderItem = actualFirstOrder.Items[0];
-
- Assert.Multiple(() =>
- {
- Assert.That(actualFirstOrderItem.ItemId, Is.EqualTo(expectedFirstOrderItem.ItemId));
- Assert.That(actualFirstOrderItem.ItemName, Is.EqualTo(expectedFirstOrderItem.ItemName));
- Assert.That(actualFirstOrderItem.Quantity, Is.EqualTo(expectedFirstOrderItem.Quantity));
- Assert.That(actualFirstOrderItem.PricePerUnit, Is.EqualTo(expectedFirstOrderItem.PricePerUnit));
- Assert.That(actualFirstOrderItem.Total, Is.EqualTo(expectedFirstOrderItem.Total));
- });
- }
-
- [Test, Order(3)]
- public async Task GivenThatICreatedAnOrder_WhenIGetTheSelectedItem_ThenTheAmountShouldBeUpdated()
- {
- // Arrange
- var expected = new ItemDto() { Id = Guid.Parse("111f9883-4b53-41eb-bcc3-8f4e6f29edf6"), Name = "Monitor", Quantity = 8, Price = 119.99 };
-
- // Act
- var response = await _httpClient.GetAsync($"/api/Item/{expected.Id}");
-
- // Assert
- Assert.That(response, Is.Not.Null);
- Assert.DoesNotThrow(() => response.EnsureSuccessStatusCode());
- Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
-
- var actual = await response.Content.ReadFromJsonAsync();
-
- Assert.Multiple(() =>
- {
- Assert.That(actual, Is.Not.Null);
- Assert.That(expected.Id, Is.EqualTo(actual!.Id));
- Assert.That(expected.Name, Is.EqualTo(actual.Name));
- Assert.That(expected.Quantity, Is.EqualTo(actual.Quantity));
- Assert.That(expected.Price, Is.EqualTo(actual.Price));
- Assert.That(expected.IsOutOfStock, Is.EqualTo(actual.IsOutOfStock));
- });
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.Tests/IntergrationTests/TestBase.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.Tests/IntergrationTests/TestBase.cs
deleted file mode 100644
index d6ddebe9e9..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.Tests/IntergrationTests/TestBase.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using Inventory.Models;
-using Microsoft.AspNetCore.Mvc.Testing;
-using Microsoft.AspNetCore.TestHost;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Moq;
-using Moq.Protected;
-using Order.Interfaces;
-using System.Net;
-using System.Text;
-using System.Text.Json;
-
-namespace ModularMonolithPatternInCSharp.Tests.IntergrationTests;
-
-internal class TestBase
-{
- protected readonly WebApplicationFactory _webApplicationFactory;
- protected readonly HttpClient _httpClient;
-
- public TestBase()
- {
- _webApplicationFactory = new WebApplicationFactory()
- .WithWebHostBuilder(builder =>
- {
- builder.ConfigureTestServices(services =>
- {
- services.RemoveAll();
-
- var item = new ItemDto
- {
- Id = Guid.Parse("111f9883-4b53-41eb-bcc3-8f4e6f29edf6"),
- Name = "Monitor",
- Price = 119.99,
- Quantity = 10
- };
-
- var inventoryRestClientMock = new Mock();
- inventoryRestClientMock.Setup(i => i.GetItem(It.IsAny()))
- .ReturnsAsync(item);
- services.AddSingleton(inventoryRestClientMock.Object);
-
- });
- });
- _httpClient = _webApplicationFactory.CreateClient();
- }
-
-
- [OneTimeTearDown]
- public void TearDown()
- {
- _webApplicationFactory.Dispose();
- _httpClient.Dispose();
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.Tests/ModularMonolithPatternInCSharp.Tests.csproj b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.Tests/ModularMonolithPatternInCSharp.Tests.csproj
deleted file mode 100644
index 5763171f60..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.Tests/ModularMonolithPatternInCSharp.Tests.csproj
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
- net8.0
- enable
- enable
-
- false
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.sln b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.sln
deleted file mode 100644
index 16a676e299..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/ModularMonolithPatternInCSharp.sln
+++ /dev/null
@@ -1,71 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.9.34607.119
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi", "WebApi\WebApi.csproj", "{19FF2516-D1B3-415B-9CBF-0274392579E9}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory", "Inventory\Inventory.csproj", "{DF75E610-59FC-47C9-A2DD-8E48348BCE3E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Order", "Order\Order.csproj", "{DE9FB0C9-0BD4-4CB4-8F52-5B9F983C6259}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inventory.Models", "Inventory.Models\Inventory.Models.csproj", "{1936F1E9-445E-407E-8D2A-86D60C9F34A2}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Order.Models", "Order.Models\Order.Models.csproj", "{E503A165-3D8C-48BD-8925-DC2CD786EA0E}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InventoryModule", "InventoryModule", "{32C44410-361C-46B6-A94F-2304E3E5B1C3}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OrderModule", "OrderModule", "{620B7DE8-76F8-402F-8028-310472719A16}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "Common\Common.csproj", "{4E98F09A-3A2F-4E93-8EDF-7FDFE4DC510D}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModularMonolithPatternInCSharp.Tests", "ModularMonolithPatternInCSharp.Tests\ModularMonolithPatternInCSharp.Tests.csproj", "{2538E071-EDDD-4705-87DC-4646EF160E5D}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {19FF2516-D1B3-415B-9CBF-0274392579E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {19FF2516-D1B3-415B-9CBF-0274392579E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {19FF2516-D1B3-415B-9CBF-0274392579E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {19FF2516-D1B3-415B-9CBF-0274392579E9}.Release|Any CPU.Build.0 = Release|Any CPU
- {DF75E610-59FC-47C9-A2DD-8E48348BCE3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DF75E610-59FC-47C9-A2DD-8E48348BCE3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DF75E610-59FC-47C9-A2DD-8E48348BCE3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DF75E610-59FC-47C9-A2DD-8E48348BCE3E}.Release|Any CPU.Build.0 = Release|Any CPU
- {DE9FB0C9-0BD4-4CB4-8F52-5B9F983C6259}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DE9FB0C9-0BD4-4CB4-8F52-5B9F983C6259}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DE9FB0C9-0BD4-4CB4-8F52-5B9F983C6259}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DE9FB0C9-0BD4-4CB4-8F52-5B9F983C6259}.Release|Any CPU.Build.0 = Release|Any CPU
- {1936F1E9-445E-407E-8D2A-86D60C9F34A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1936F1E9-445E-407E-8D2A-86D60C9F34A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1936F1E9-445E-407E-8D2A-86D60C9F34A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1936F1E9-445E-407E-8D2A-86D60C9F34A2}.Release|Any CPU.Build.0 = Release|Any CPU
- {E503A165-3D8C-48BD-8925-DC2CD786EA0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E503A165-3D8C-48BD-8925-DC2CD786EA0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E503A165-3D8C-48BD-8925-DC2CD786EA0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E503A165-3D8C-48BD-8925-DC2CD786EA0E}.Release|Any CPU.Build.0 = Release|Any CPU
- {4E98F09A-3A2F-4E93-8EDF-7FDFE4DC510D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4E98F09A-3A2F-4E93-8EDF-7FDFE4DC510D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4E98F09A-3A2F-4E93-8EDF-7FDFE4DC510D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4E98F09A-3A2F-4E93-8EDF-7FDFE4DC510D}.Release|Any CPU.Build.0 = Release|Any CPU
- {2538E071-EDDD-4705-87DC-4646EF160E5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2538E071-EDDD-4705-87DC-4646EF160E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2538E071-EDDD-4705-87DC-4646EF160E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2538E071-EDDD-4705-87DC-4646EF160E5D}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {DF75E610-59FC-47C9-A2DD-8E48348BCE3E} = {32C44410-361C-46B6-A94F-2304E3E5B1C3}
- {DE9FB0C9-0BD4-4CB4-8F52-5B9F983C6259} = {620B7DE8-76F8-402F-8028-310472719A16}
- {1936F1E9-445E-407E-8D2A-86D60C9F34A2} = {32C44410-361C-46B6-A94F-2304E3E5B1C3}
- {E503A165-3D8C-48BD-8925-DC2CD786EA0E} = {620B7DE8-76F8-402F-8028-310472719A16}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {ED086FE8-8B29-49F1-ABB2-21515A44AA9D}
- EndGlobalSection
-EndGlobal
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order.Models/Order.Models.csproj b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order.Models/Order.Models.csproj
deleted file mode 100644
index fa71b7ae6a..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order.Models/Order.Models.csproj
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- net8.0
- enable
- enable
-
-
-
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order.Models/OrderDto.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order.Models/OrderDto.cs
deleted file mode 100644
index dd1ceec515..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order.Models/OrderDto.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Order.Models;
-
-public class OrderDto
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- public double Total { get; set; }
- public List Items { get; set; } = [];
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order.Models/OrderItemDto.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order.Models/OrderItemDto.cs
deleted file mode 100644
index 1591233576..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order.Models/OrderItemDto.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Order.Models;
-
-public class OrderItemDto
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- public Guid ItemId { get; set; }
- public string ItemName { get; set; } = string.Empty;
- public int Quantity { get; set; }
- public double PricePerUnit { get; set; }
- public double Total { get; set; }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Controllers/OrderController.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Controllers/OrderController.cs
deleted file mode 100644
index bd3654e888..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Controllers/OrderController.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Order.Interfaces;
-using Order.Models;
-using Order.ViewModels;
-
-namespace Order.Controllers;
-
-[Route("api/[controller]")]
-[ApiController]
-public class OrderController(IOrderService orderService) : ControllerBase
-{
- [HttpPost]
- public async Task AddAsync(OrderViewModel viewModel)
- {
- try
- {
- await orderService.AddAsync(new OrderDto
- {
- Items = viewModel.Items.Select(i => new OrderItemDto
- {
- ItemId = i.ItemId,
- Quantity = i.Quantity
- }).ToList()
- });
-
- return Ok();
- }
- catch (Exception ex)
- {
- return BadRequest(ex.Message);
- }
- }
-
- [HttpGet]
- public async Task GetAllAsync()
- {
- try
- {
- var orders = await orderService.GetAllAsync();
-
- return Ok(orders);
- }
- catch (Exception ex)
- {
- return BadRequest(ex.Message);
- }
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Entities/Order.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Entities/Order.cs
deleted file mode 100644
index 8c69765970..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Entities/Order.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Order.Models;
-
-namespace Order.Entities;
-
-internal class Order
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- public List Items { get; set; } = [];
-
- public OrderDto ToDto()
- {
- return new OrderDto
- {
- Id = Id,
- Items = Items.Select(x => x.ToDto()).ToList(),
- Total = Items.Sum(x => x.Total)
- };
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Entities/OrderItem.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Entities/OrderItem.cs
deleted file mode 100644
index ccc30f56f4..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Entities/OrderItem.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using Order.Models;
-
-namespace Order.Entities;
-
-internal class OrderItem
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- public Guid ItemId { get; set; }
- public int Quantity { get; set; }
- public double PricePerUnit { get; set; }
- public double Total { get; set; }
-
- public OrderItemDto ToDto()
- {
- return new OrderItemDto
- {
- Id = Id,
- ItemId = ItemId,
- Quantity = Quantity,
- PricePerUnit = PricePerUnit,
- Total = Total
- };
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Interfaces/IInventoryRabbitMqClient.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Interfaces/IInventoryRabbitMqClient.cs
deleted file mode 100644
index ae087946e0..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Interfaces/IInventoryRabbitMqClient.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using Inventory.Models;
-
-namespace Order.Interfaces;
-
-public interface IInventoryRabbitMqClient
-{
- void UpdateQuantity(UpdateQuantityDto updateQuantityDto);
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Interfaces/IInventoryRestClient.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Interfaces/IInventoryRestClient.cs
deleted file mode 100644
index dc11610008..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Interfaces/IInventoryRestClient.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using Inventory.Models;
-
-namespace Order.Interfaces;
-
-public interface IInventoryRestClient
-{
- Task GetItem(Guid id);
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Interfaces/IOrderRepository.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Interfaces/IOrderRepository.cs
deleted file mode 100644
index 6fd59b20c7..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Interfaces/IOrderRepository.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Order.Models;
-
-namespace Order.Interfaces;
-
-public interface IOrderRepository
-{
- void Add(OrderDto orderDto);
- List GetAll();
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Interfaces/IOrderService.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Interfaces/IOrderService.cs
deleted file mode 100644
index 75d85d3e5f..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Interfaces/IOrderService.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Order.Models;
-
-namespace Order.Interfaces;
-
-public interface IOrderService
-{
- Task AddAsync(OrderDto orderDto);
- Task> GetAllAsync();
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Order.csproj b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Order.csproj
deleted file mode 100644
index d597fc2194..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Order.csproj
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
- net8.0
- enable
- enable
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Properties/launchSettings.json b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Properties/launchSettings.json
deleted file mode 100644
index bcc9607baa..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Properties/launchSettings.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "profiles": {
- "Order": {
- "commandName": "Project",
- "launchBrowser": true,
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- },
- "applicationUrl": "https://localhost:52176;http://localhost:52177"
- }
- }
-}
\ No newline at end of file
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Repositories/OrderRepository.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Repositories/OrderRepository.cs
deleted file mode 100644
index 2e0350b4e1..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Repositories/OrderRepository.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Order.Interfaces;
-using Order.Models;
-
-namespace Order.Repositories;
-
-internal class OrderRepository : IOrderRepository
-{
- private static readonly List _orders = [];
-
- public void Add(OrderDto orderDto)
- {
- var order = new Entities.Order
- {
- Items = orderDto.Items.Select(i => new Entities.OrderItem
- {
- ItemId = i.ItemId,
- Id = i.Id,
- PricePerUnit = i.PricePerUnit,
- Quantity = i.Quantity,
- Total = i.Total,
- }).ToList()
- };
-
- _orders.Add(order);
- }
-
- public List GetAll()
- {
- return _orders.Select(i => i.ToDto()).ToList();
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/ServiceCollectionExtensions.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/ServiceCollectionExtensions.cs
deleted file mode 100644
index c5018d4f32..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Order.Interfaces;
-using Order.Repositories;
-using Order.Services;
-
-namespace Order;
-
-public static class ServiceCollectionExtensions
-{
- public static void AddOrderModule(this IServiceCollection services)
- {
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Services/InventoryRabbitMqClient.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Services/InventoryRabbitMqClient.cs
deleted file mode 100644
index 5f73f21c50..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Services/InventoryRabbitMqClient.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Common.RabbitMq;
-using Inventory.Models;
-using Microsoft.Extensions.Options;
-using Order.Interfaces;
-using RabbitMQ.Client;
-using System.Text;
-using System.Text.Json;
-
-namespace Order.Services;
-
-internal class InventoryRabbitMqClient(IOptions rabbitMqConfiguration, IRabbitMqConnectionManager rabbitMqConnectionManager) : IInventoryRabbitMqClient
-{
- public void UpdateQuantity(UpdateQuantityDto updateQuantityDto)
- {
- var serializedMessage = JsonSerializer.Serialize(updateQuantityDto);
- var body = Encoding.UTF8.GetBytes(serializedMessage);
-
- var queueName = rabbitMqConfiguration.Value.QueueName;
-
- rabbitMqConnectionManager.Channel.BasicPublish(exchange: string.Empty,
- routingKey: queueName,
- basicProperties: null,
- body: body);
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Services/InventoryRestClient.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Services/InventoryRestClient.cs
deleted file mode 100644
index ca0b0f284a..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Services/InventoryRestClient.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using Inventory.Models;
-using Microsoft.Extensions.Configuration;
-using Order.Interfaces;
-using System.Net.Http.Json;
-
-namespace Order.Services;
-
-internal class InventoryRestClient(IHttpClientFactory clientFactory, IConfiguration configuration) : IInventoryRestClient
-{
- public async Task GetItem(Guid id)
- {
- var httpClient = clientFactory.CreateClient();
- httpClient.BaseAddress = new Uri(configuration["BaseAddress"]!);
- var result = await httpClient.GetAsync($"/api/Item/{id}");
-
- result.EnsureSuccessStatusCode();
-
- return await result.Content.ReadFromJsonAsync();
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Services/OrderService.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Services/OrderService.cs
deleted file mode 100644
index d22daaff41..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/Services/OrderService.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using Order.Interfaces;
-using Order.Models;
-
-namespace Order.Services;
-
-internal class OrderService(
- IOrderRepository orderRepository,
- IInventoryRabbitMqClient inventoryRabbitMqClient,
- IInventoryRestClient inventoryRestClient) : IOrderService
-{
- public async Task AddAsync(OrderDto orderDto)
- {
- foreach (var itemModel in orderDto.Items)
- {
- var item = await inventoryRestClient.GetItem(itemModel.ItemId) ??
- throw new InvalidOperationException($"The requested item '{itemModel.ItemId} was not found.'");
-
- if (item.Quantity < itemModel.Quantity)
- throw new InvalidOperationException($"There isn't enough stock for item '{itemModel.ItemId}'.");
-
- itemModel.PricePerUnit = item.Price;
- itemModel.Total = itemModel.Quantity * item.Price;
-
- inventoryRabbitMqClient.UpdateQuantity(new Inventory.Models.UpdateQuantityDto
- {
- ItemId = itemModel.ItemId,
- Amount = itemModel.Quantity * -1
- });
- }
-
- orderDto.Total = orderDto.Items.Sum(i => i.Total);
-
- orderRepository.Add(orderDto);
- }
-
- public async Task> GetAllAsync()
- {
- var orders = orderRepository.GetAll();
-
- foreach (var orderItem in orders.SelectMany(i => i.Items))
- {
- var item = await inventoryRestClient.GetItem(orderItem.ItemId) ??
- throw new InvalidOperationException($"The requested item '{orderItem.ItemId} was not found.'");
- orderItem.ItemName = item.Name;
- }
-
- return orders;
- }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/ViewModels/OrderItemViewModel.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/ViewModels/OrderItemViewModel.cs
deleted file mode 100644
index 0e84c24495..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/ViewModels/OrderItemViewModel.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Order.ViewModels;
-
-public class OrderItemViewModel
-{
- public Guid ItemId { get; set; }
- public int Quantity { get; set; }
-}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/ViewModels/OrderViewModel.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/ViewModels/OrderViewModel.cs
deleted file mode 100644
index 650d5808df..0000000000
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Order/ViewModels/OrderViewModel.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Order.ViewModels;
-
-public class OrderViewModel
-{
- public List Items { get; set; } = [];
-}
\ No newline at end of file
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/InProcessEventBus.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/InProcessEventBus.cs
new file mode 100644
index 0000000000..86ef0c0e1a
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/InProcessEventBus.cs
@@ -0,0 +1,44 @@
+using System.Threading.Channels;
+using Shop.Shared;
+
+namespace Shop.Api;
+
+public delegate Task EventDelivery(IServiceProvider services, CancellationToken cancellationToken);
+
+public sealed class InProcessEventBus : IEventBus
+{
+ private readonly Channel _deliveries = Channel.CreateUnbounded();
+
+ public ChannelReader Deliveries => _deliveries.Reader;
+
+ public ValueTask PublishAsync(TEvent integrationEvent, CancellationToken cancellationToken = default)
+ where TEvent : notnull =>
+ _deliveries.Writer.WriteAsync(async (services, token) =>
+ {
+ foreach (var handler in services.GetServices>())
+ await handler.HandleAsync(integrationEvent, token);
+ }, cancellationToken);
+}
+
+public sealed class EventDispatcher(
+ InProcessEventBus eventBus,
+ IServiceScopeFactory scopeFactory,
+ ILogger logger) : BackgroundService
+{
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ await foreach (var delivery in eventBus.Deliveries.ReadAllAsync(stoppingToken))
+ {
+ using var scope = scopeFactory.CreateScope();
+
+ try
+ {
+ await delivery(scope.ServiceProvider, stoppingToken);
+ }
+ catch (Exception exception)
+ {
+ logger.LogError(exception, "An integration event handler failed.");
+ }
+ }
+ }
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/Program.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/Program.cs
new file mode 100644
index 0000000000..5ec583ecf5
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/Program.cs
@@ -0,0 +1,24 @@
+using Shop.Api;
+using Shop.Inventory;
+using Shop.Orders;
+using Shop.Shared;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddProblemDetails();
+
+builder.Services.AddSingleton();
+builder.Services.AddSingleton(sp => sp.GetRequiredService());
+builder.Services.AddHostedService();
+
+builder.Services.AddInventoryModule();
+builder.Services.AddOrdersModule();
+
+var app = builder.Build();
+
+app.UseExceptionHandler();
+
+app.MapInventoryEndpoints();
+app.MapOrdersEndpoints();
+
+app.Run();
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/Properties/launchSettings.json b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/Properties/launchSettings.json
new file mode 100644
index 0000000000..edd37eea69
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/Properties/launchSettings.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:5170",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:7053;http://localhost:5170",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/Shop.Api.csproj b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/Shop.Api.csproj
new file mode 100644
index 0000000000..b865b17d40
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/Shop.Api.csproj
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/Shop.Api.http b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/Shop.Api.http
new file mode 100644
index 0000000000..5dca3834b0
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/Shop.Api.http
@@ -0,0 +1,45 @@
+GET http://localhost:5000/api/inventory/items/1
+
+###
+
+POST http://localhost:5000/api/orders
+Content-Type: application/json
+
+{ "itemId": 1, "quantity": 2 }
+
+###
+
+GET http://localhost:5000/api/inventory/items/1
+
+###
+
+POST http://localhost:5000/api/orders
+Content-Type: application/json
+
+{ "itemId": 2, "quantity": 3 }
+
+###
+
+POST http://localhost:5000/api/orders
+Content-Type: application/json
+
+{ "itemId": 1, "quantity": 0 }
+
+###
+
+POST http://localhost:5000/api/orders
+Content-Type: application/json
+
+{ "itemId": 1, "quantity": 2 }
+
+###
+
+POST http://localhost:5000/api/orders/1/cancel
+
+###
+
+GET http://localhost:5000/api/inventory/items/1
+
+###
+
+POST http://localhost:5000/api/orders/1/cancel
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/appsettings.Development.json b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/appsettings.Development.json
new file mode 100644
index 0000000000..0c208ae918
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/appsettings.json b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/appsettings.json
new file mode 100644
index 0000000000..10f68b8c8b
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Api/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Contracts/Inventory/IInventoryModule.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Contracts/Inventory/IInventoryModule.cs
new file mode 100644
index 0000000000..c524b7f846
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Contracts/Inventory/IInventoryModule.cs
@@ -0,0 +1,11 @@
+using Shop.Shared;
+
+namespace Shop.Contracts.Inventory;
+
+public interface IInventoryModule
+{
+ Task> ReserveStockAsync(
+ int itemId, int quantity, CancellationToken cancellationToken = default);
+}
+
+public sealed record StockReservation(int ItemId, string ItemName, decimal UnitPrice, int Quantity);
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Contracts/Orders/OrderCancelled.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Contracts/Orders/OrderCancelled.cs
new file mode 100644
index 0000000000..37627cccfa
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Contracts/Orders/OrderCancelled.cs
@@ -0,0 +1,3 @@
+namespace Shop.Contracts.Orders;
+
+public sealed record OrderCancelled(int OrderId, int ItemId, int Quantity);
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory.Models/Inventory.Models.csproj b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Contracts/Shop.Contracts.csproj
similarity index 52%
rename from csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory.Models/Inventory.Models.csproj
rename to csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Contracts/Shop.Contracts.csproj
index fa71b7ae6a..1320023605 100644
--- a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Inventory.Models/Inventory.Models.csproj
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Contracts/Shop.Contracts.csproj
@@ -1,7 +1,11 @@
+
+
+
+
- net8.0
+ net10.0
enable
enable
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Application/IItemRepository.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Application/IItemRepository.cs
new file mode 100644
index 0000000000..34c20a4002
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Application/IItemRepository.cs
@@ -0,0 +1,10 @@
+using Shop.Inventory.Domain;
+
+namespace Shop.Inventory.Application;
+
+internal interface IItemRepository
+{
+ Task- GetByIdAsync(int itemId, CancellationToken cancellationToken = default);
+
+ Task SaveChangesAsync(CancellationToken cancellationToken = default);
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Application/InventoryService.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Application/InventoryService.cs
new file mode 100644
index 0000000000..2c3af24ed0
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Application/InventoryService.cs
@@ -0,0 +1,36 @@
+using Shop.Contracts.Inventory;
+using Shop.Inventory.Domain;
+using Shop.Shared;
+
+namespace Shop.Inventory.Application;
+
+internal sealed record ItemResponse(int Id, string Name, decimal UnitPrice, int InStock);
+
+internal sealed class InventoryService(IItemRepository items) : IInventoryModule
+{
+ public async Task> ReserveStockAsync(
+ int itemId, int quantity, CancellationToken cancellationToken = default)
+ {
+ var item = await items.GetByIdAsync(itemId, cancellationToken);
+ if (item is null)
+ return ItemErrors.NotFound(itemId);
+
+ var reservation = item.Reserve(quantity);
+ if (!reservation.IsSuccess)
+ return reservation.Error!;
+
+ await items.SaveChangesAsync(cancellationToken);
+
+ return new StockReservation(item.Id, item.Name, item.UnitPrice, quantity);
+ }
+
+ public async Task> GetItemAsync(
+ int itemId, CancellationToken cancellationToken = default)
+ {
+ var item = await items.GetByIdAsync(itemId, cancellationToken);
+ if (item is null)
+ return ItemErrors.NotFound(itemId);
+
+ return new ItemResponse(item.Id, item.Name, item.UnitPrice, item.InStock);
+ }
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Application/ReleaseStockWhenOrderCancelled.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Application/ReleaseStockWhenOrderCancelled.cs
new file mode 100644
index 0000000000..acdd2ad522
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Application/ReleaseStockWhenOrderCancelled.cs
@@ -0,0 +1,19 @@
+using Shop.Contracts.Orders;
+using Shop.Shared;
+
+namespace Shop.Inventory.Application;
+
+internal sealed class ReleaseStockWhenOrderCancelled(IItemRepository items)
+ : IIntegrationEventHandler
+{
+ public async Task HandleAsync(
+ OrderCancelled integrationEvent, CancellationToken cancellationToken = default)
+ {
+ var item = await items.GetByIdAsync(integrationEvent.ItemId, cancellationToken)
+ ?? throw new InvalidOperationException($"Item {integrationEvent.ItemId} was not found.");
+
+ item.Release(integrationEvent.Quantity);
+
+ await items.SaveChangesAsync(cancellationToken);
+ }
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Domain/Item.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Domain/Item.cs
new file mode 100644
index 0000000000..3088d4df00
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Domain/Item.cs
@@ -0,0 +1,39 @@
+using Shop.Shared;
+
+namespace Shop.Inventory.Domain;
+
+internal sealed class Item(int id, string name, decimal unitPrice, int inStock)
+{
+ public int Id { get; } = id;
+ public string Name { get; } = name;
+ public decimal UnitPrice { get; } = unitPrice;
+ public int InStock { get; private set; } = inStock;
+
+ public Result Reserve(int quantity)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(quantity);
+
+ if (quantity > InStock)
+ return ItemErrors.OutOfStock(Id, InStock, quantity);
+
+ InStock -= quantity;
+
+ return Result.Success();
+ }
+
+ public void Release(int quantity)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(quantity);
+
+ InStock += quantity;
+ }
+}
+
+internal static class ItemErrors
+{
+ public static Error NotFound(int itemId) =>
+ new("Item.NotFound", $"Item {itemId} was not found.", ErrorType.NotFound);
+
+ public static Error OutOfStock(int itemId, int inStock, int requested) =>
+ new("Item.OutOfStock", $"Only {inStock} of item {itemId} left, {requested} requested.", ErrorType.Conflict);
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Infrastructure/InMemoryItemRepository.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Infrastructure/InMemoryItemRepository.cs
new file mode 100644
index 0000000000..d4d8c93c63
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Infrastructure/InMemoryItemRepository.cs
@@ -0,0 +1,19 @@
+using Shop.Inventory.Application;
+using Shop.Inventory.Domain;
+
+namespace Shop.Inventory.Infrastructure;
+
+internal sealed class InMemoryItemRepository : IItemRepository
+{
+ private readonly Dictionary _items = new()
+ {
+ [1] = new Item(1, "Mechanical keyboard", 89.99m, inStock: 10),
+ [2] = new Item(2, "Rubber duck", 4.99m, inStock: 2)
+ };
+
+ public Task
- GetByIdAsync(int itemId, CancellationToken cancellationToken = default) =>
+ Task.FromResult(_items.GetValueOrDefault(itemId));
+
+ public Task SaveChangesAsync(CancellationToken cancellationToken = default) =>
+ Task.CompletedTask;
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/InventoryModule.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/InventoryModule.cs
new file mode 100644
index 0000000000..e5dd9fdaee
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/InventoryModule.cs
@@ -0,0 +1,39 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.DependencyInjection;
+using Shop.Contracts.Inventory;
+using Shop.Contracts.Orders;
+using Shop.Inventory.Application;
+using Shop.Inventory.Infrastructure;
+using Shop.Shared;
+
+namespace Shop.Inventory;
+
+public static class InventoryModule
+{
+ public static IServiceCollection AddInventoryModule(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ services.AddScoped();
+ services.AddScoped(sp => sp.GetRequiredService());
+ services.AddScoped, ReleaseStockWhenOrderCancelled>();
+
+ return services;
+ }
+
+ public static void MapInventoryEndpoints(this IEndpointRouteBuilder app)
+ {
+ var inventory = app.MapGroup("/api/inventory");
+
+ inventory.MapGet("/items/{itemId:int}", async (
+ int itemId,
+ InventoryService service,
+ CancellationToken cancellationToken) =>
+ {
+ var result = await service.GetItemAsync(itemId, cancellationToken);
+
+ return result.IsSuccess ? Results.Ok(result.Value) : result.ToProblem();
+ });
+ }
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Shop.Inventory.csproj b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Shop.Inventory.csproj
new file mode 100644
index 0000000000..f7a047ba0f
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Inventory/Shop.Inventory.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/Features/CancelOrder.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/Features/CancelOrder.cs
new file mode 100644
index 0000000000..aef6f15466
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/Features/CancelOrder.cs
@@ -0,0 +1,41 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Shop.Contracts.Orders;
+using Shop.Shared;
+
+namespace Shop.Orders.Features;
+
+internal static class CancelOrder
+{
+ public sealed record Response(int OrderId, string Status);
+
+ public sealed class Handler(OrderStore orders, IEventBus eventBus)
+ {
+ public async Task> HandleAsync(
+ int orderId, CancellationToken cancellationToken = default)
+ {
+ var order = orders.Find(orderId);
+ if (order is null)
+ return OrderErrors.NotFound(orderId);
+
+ var cancellation = order.Cancel();
+ if (!cancellation.IsSuccess)
+ return cancellation.Error!;
+
+ await eventBus.PublishAsync(
+ new OrderCancelled(order.Id, order.ItemId, order.Quantity), cancellationToken);
+
+ return new Response(order.Id, order.Status.ToString());
+ }
+ }
+
+ public static void MapCancelOrder(this IEndpointRouteBuilder app) =>
+ app.MapPost("/{orderId:int}/cancel", async (
+ int orderId, Handler handler, CancellationToken cancellationToken) =>
+ {
+ var result = await handler.HandleAsync(orderId, cancellationToken);
+
+ return result.IsSuccess ? Results.Ok(result.Value) : result.ToProblem();
+ });
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/Features/PlaceOrder.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/Features/PlaceOrder.cs
new file mode 100644
index 0000000000..fe6d2705d6
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/Features/PlaceOrder.cs
@@ -0,0 +1,40 @@
+using System.ComponentModel.DataAnnotations;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Shop.Contracts.Inventory;
+using Shop.Shared;
+
+namespace Shop.Orders.Features;
+
+internal static class PlaceOrder
+{
+ public sealed record Request(int ItemId, [property: Range(1, 10)] int Quantity);
+
+ public sealed record Response(int OrderId, string ItemName, int Quantity, decimal Total);
+
+ public sealed class Handler(IInventoryModule inventory, OrderStore orders)
+ {
+ public async Task> HandleAsync(
+ Request request, CancellationToken cancellationToken = default)
+ {
+ var reservation = await inventory.ReserveStockAsync(
+ request.ItemId, request.Quantity, cancellationToken);
+ if (!reservation.IsSuccess)
+ return reservation.Error!;
+
+ var stock = reservation.Value;
+ var order = orders.Add(stock.ItemId, stock.Quantity, stock.UnitPrice * stock.Quantity);
+
+ return new Response(order.Id, stock.ItemName, order.Quantity, order.Total);
+ }
+ }
+
+ public static void MapPlaceOrder(this IEndpointRouteBuilder app) =>
+ app.MapPost("/", async (Request request, Handler handler, CancellationToken cancellationToken) =>
+ {
+ var result = await handler.HandleAsync(request, cancellationToken);
+
+ return result.IsSuccess ? Results.Ok(result.Value) : result.ToProblem();
+ });
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/Order.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/Order.cs
new file mode 100644
index 0000000000..a5c706013d
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/Order.cs
@@ -0,0 +1,37 @@
+using Shop.Shared;
+
+namespace Shop.Orders;
+
+internal enum OrderStatus
+{
+ Placed,
+ Cancelled
+}
+
+internal sealed class Order(int id, int itemId, int quantity, decimal total)
+{
+ public int Id { get; } = id;
+ public int ItemId { get; } = itemId;
+ public int Quantity { get; } = quantity;
+ public decimal Total { get; } = total;
+ public OrderStatus Status { get; private set; } = OrderStatus.Placed;
+
+ public Result Cancel()
+ {
+ if (Status == OrderStatus.Cancelled)
+ return OrderErrors.AlreadyCancelled(Id);
+
+ Status = OrderStatus.Cancelled;
+
+ return Result.Success();
+ }
+}
+
+internal static class OrderErrors
+{
+ public static Error NotFound(int orderId) =>
+ new("Order.NotFound", $"Order {orderId} was not found.", ErrorType.NotFound);
+
+ public static Error AlreadyCancelled(int orderId) =>
+ new("Order.AlreadyCancelled", $"Order {orderId} is already cancelled.", ErrorType.Conflict);
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/OrderStore.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/OrderStore.cs
new file mode 100644
index 0000000000..c8faa59567
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/OrderStore.cs
@@ -0,0 +1,19 @@
+using System.Collections.Concurrent;
+
+namespace Shop.Orders;
+
+internal sealed class OrderStore
+{
+ private readonly ConcurrentDictionary _orders = new();
+ private int _lastId;
+
+ public Order Add(int itemId, int quantity, decimal total)
+ {
+ var order = new Order(Interlocked.Increment(ref _lastId), itemId, quantity, total);
+ _orders[order.Id] = order;
+
+ return order;
+ }
+
+ public Order? Find(int orderId) => _orders.GetValueOrDefault(orderId);
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/OrdersModule.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/OrdersModule.cs
new file mode 100644
index 0000000000..cbd2fefe25
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/OrdersModule.cs
@@ -0,0 +1,27 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.DependencyInjection;
+using Shop.Orders.Features;
+
+namespace Shop.Orders;
+
+public static class OrdersModule
+{
+ public static IServiceCollection AddOrdersModule(this IServiceCollection services)
+ {
+ services.AddValidation();
+ services.AddSingleton();
+ services.AddScoped();
+ services.AddScoped();
+
+ return services;
+ }
+
+ public static void MapOrdersEndpoints(this IEndpointRouteBuilder app)
+ {
+ var orders = app.MapGroup("/api/orders");
+
+ orders.MapPlaceOrder();
+ orders.MapCancelOrder();
+ }
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/Shop.Orders.csproj b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/Shop.Orders.csproj
new file mode 100644
index 0000000000..d19cffdb18
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Orders/Shop.Orders.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Shared/EventBus.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Shared/EventBus.cs
new file mode 100644
index 0000000000..34ef1abead
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Shared/EventBus.cs
@@ -0,0 +1,12 @@
+namespace Shop.Shared;
+
+public interface IEventBus
+{
+ ValueTask PublishAsync(TEvent integrationEvent, CancellationToken cancellationToken = default)
+ where TEvent : notnull;
+}
+
+public interface IIntegrationEventHandler
+{
+ Task HandleAsync(TEvent integrationEvent, CancellationToken cancellationToken = default);
+}
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Shared/Result.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Shared/Result.cs
new file mode 100644
index 0000000000..5ed6460606
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Shared/Result.cs
@@ -0,0 +1,39 @@
+namespace Shop.Shared;
+
+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/ModularMonolithPatternInCSharp/Shop.Shared/ResultExtensions.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Shared/ResultExtensions.cs
new file mode 100644
index 0000000000..515b93ca53
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Shared/ResultExtensions.cs
@@ -0,0 +1,21 @@
+using Microsoft.AspNetCore.Http;
+
+namespace Shop.Shared;
+
+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/ModularMonolithPatternInCSharp/Shop.Shared/Shop.Shared.csproj b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Shared/Shop.Shared.csproj
new file mode 100644
index 0000000000..418d4b41a0
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Shared/Shop.Shared.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Tests/CancelOrderTests.cs b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Tests/CancelOrderTests.cs
new file mode 100644
index 0000000000..cf64ea22d4
--- /dev/null
+++ b/csharp-architectural-patterns/ModularMonolithPatternInCSharp/Shop.Tests/CancelOrderTests.cs
@@ -0,0 +1,36 @@
+using Shop.Contracts.Orders;
+using Shop.Orders;
+using Shop.Orders.Features;
+using Shop.Shared;
+
+namespace Shop.Tests;
+
+public class CancelOrderTests
+{
+ [Fact]
+ public async Task HandleAsync_WhenOrderIsPlaced_PublishesOrderCancelled()
+ {
+ var orders = new OrderStore();
+ var order = orders.Add(itemId: 1, quantity: 2, total: 179.98m);
+ var eventBus = new FakeEventBus();
+ var handler = new CancelOrder.Handler(orders, eventBus);
+
+ var result = await handler.HandleAsync(order.Id, TestContext.Current.CancellationToken);
+
+ Assert.True(result.IsSuccess);
+ Assert.Equal(new OrderCancelled(order.Id, ItemId: 1, Quantity: 2), Assert.Single(eventBus.Published));
+ }
+}
+
+internal sealed class FakeEventBus : IEventBus
+{
+ public List