A single deployable, split into modules per bounded context (Orders, Catalog, Pricing, Shipping).
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); |