EngineeringSenior DeveloperArchitecture and Patterns

03 - Modular Monolith Blueprint

Sức mạnh của Monolith (đơn giản, strong consistency) kết hợp tổ chức code kiểu Microservices (module độc lập). Con đường tiến hóa tự nhiên nhất từ Monolith sang Microservices.


Tổng Quan

Key Point: 1 Deployment, NHIỀU modules, mỗi module có schema riêng trong cùng DB. Modules giao tiếp qua Integration Events, KHÔNG gọi trực tiếp internal services của nhau.


Khi Nào Dùng / Không Dùng

✅ Dùng khi❌ Không dùng khi
Team 5-15 devs, growingSolo dev hoặc < 3 devs
Muốn chuẩn bị cho future MicroservicesĐã cần scale từng service RIÊNG BIỆT
Domain phức tạp, nhiều bounded contextsSimple CRUD, 1 bounded context
Cần strong consistency nhưng muốn module isolationĐã có DevOps mature cho k8s

Dự án hay dùng: SaaS products, ERP, eCommerce giai đoạn growth, Insurance platforms.


Project Structure

MyApp/
├── MyApp.sln
├── src/
│   ├── Bootstrapper/
│   │   └── MyApp.Host/                    ← Single entry point
│   │       ├── Program.cs
│   │       └── appsettings.json
│   │
│   ├── Shared/
│   │   └── MyApp.Shared/                  ← Shared Kernel
│   │       ├── Abstractions/
│   │       │   ├── IIntegrationEvent.cs
│   │       │   └── IModule.cs
│   │       └── Contracts/                  ← Shared DTOs (inter-module)
│   │           └── OrderCreatedIntegrationEvent.cs
│   │
│   ├── Modules/
│   │   ├── Orders/
│   │   │   ├── MyApp.Modules.Orders.Domain/
│   │   │   ├── MyApp.Modules.Orders.Application/
│   │   │   ├── MyApp.Modules.Orders.Infrastructure/
│   │   │   └── MyApp.Modules.Orders.API/
│   │   │       └── OrdersModule.cs        ← IModule implementation
│   │   │
│   │   ├── Inventory/
│   │   │   ├── MyApp.Modules.Inventory.Domain/
│   │   │   ├── MyApp.Modules.Inventory.Application/
│   │   │   ├── MyApp.Modules.Inventory.Infrastructure/
│   │   │   └── MyApp.Modules.Inventory.API/
│   │   │
│   │   └── Shipping/
│   │       └── ... (same structure)
│   │
└── tests/
    ├── Modules/
    │   ├── Orders.Tests/
    │   └── Inventory.Tests/
    └── Integration/
        └── ModuleCommunication.Tests/

Module Registration & Isolation

csharp
// Shared/IModule.cs
public interface IModule
{
    void RegisterServices(IServiceCollection services, IConfiguration config);
    void MapEndpoints(IEndpointRouteBuilder app);
}

// Orders/OrdersModule.cs
public class OrdersModule : IModule
{
    public void RegisterServices(IServiceCollection services, IConfiguration config)
    {
        services.AddDbContext<OrdersDbContext>(o =>
            o.UseSqlServer(config.GetConnectionString("Default"),
                sql => sql.MigrationsHistoryTable("__EFMigrationsHistory", "orders")));
                // ↑ Schema riêng "orders" - isolation ở DB level

        services.AddMediatR(cfg =>
            cfg.RegisterServicesFromAssembly(typeof(OrdersModule).Assembly));
    }

    public void MapEndpoints(IEndpointRouteBuilder app)
    {
        var group = app.MapGroup("/api/orders").WithTags("Orders");
        group.MapPost("/", CreateOrder.Handle);
        group.MapGet("/{id}", GetOrderById.Handle);
    }
}

// Host/Program.cs - Auto-discover modules
var modules = DiscoverModules(); // Scan assemblies for IModule
foreach (var module in modules)
    module.RegisterServices(builder.Services, builder.Configuration);

var app = builder.Build();
foreach (var module in modules)
    module.MapEndpoints(app);

Module Communication (Integration Events)

csharp
// KHÔNG gọi InventoryService trực tiếp!
// ❌ var inventory = _inventoryService.ReserveStock(productId, qty);
// ✅ Publish Integration Event
await _mediator.Publish(new OrderCreatedIntegrationEvent(order.Id, order.ProductId, order.Quantity));

Tiến hóa → Microservices

Khi module X cần scale riêng:
  1. Extract module X ra standalone service
  2. Thay In-Process Event Bus → Message Broker (RabbitMQ/Azure Service Bus)
  3. Tách database schema ra DB riêng
  → Module code gần như KHÔNG ĐỔI (chỉ đổi infra wiring)