Modular monolith in .NET: bounded-context modules, separate DbContexts, integration events

.NET Architecture & Design Patterns November 13, 2026 csharp

A single deployable, split into modules per bounded context (Orders, Catalog, Pricing, Shipping).

csharp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// ======================================================================
// Q: What is a modular monolith?
//
// A single deployable, split into modules per bounded context (Orders, Catalog,
// Pricing, Shipping). Each module has its own data and a public contract. No
// module reads another module's tables.
// - Internal classes are marked internal. The boundaries are enforced with
//   architecture tests.
// - If a module later needs independent scaling, it can be extracted into a
//   microservice with few changes: the boundaries already exist.
// ======================================================================

// Orders does not JOIN Catalog's tables: it asks through a public contract
public interface ICatalogModule
{
    Task<ProductInfo?> GetProductAsync(string sku, CancellationToken ct);
}

// Each module with its own DbContext and schema
public class OrdersDbContext(DbContextOptions<OrdersDbContext> o) : DbContext(o)
{
    protected override void OnModelCreating(ModelBuilder b) => b.HasDefaultSchema("orders");
}

// Asynchronous communication between modules via integration events (in-process or outbox)
public record OrderPlacedIntegrationEvent(int OrderId, string CustomerNumber);