بنقرة واحدة
modular-monolith
Use when structuring a modular monolith with module isolation and inter-module communication.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Use when structuring a modular monolith with module isolation and inter-module communication.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Use when designing gRPC services, proto files, or adding gRPC-Web or JSON transcoding.
Use when writing async code, propagating CancellationTokens, or fixing async/await pitfalls.
Use when applying or enforcing C# coding style — namespaces, sealed classes, var usage, XML docs.
Use when applying modern C# idioms — records, pattern matching, primary constructors, collection expressions.
Use when registering services, choosing lifetimes, or implementing DI patterns like decorator or keyed services.
Use when selecting or implementing design patterns in C# — factory, builder, strategy, decorator, or mediator.
| name | modular-monolith |
| description | Use when structuring a modular monolith with module isolation and inter-module communication. |
| metadata | {"category":"architecture","agent":"dotnet-architect"} |
| when_to_use | When designing modular monolith structure, module isolation, or inter-module communication |
src/
{Company}.{Domain}.WebApi/ # Host — composition root
{Company}.{Domain}.SharedKernel/ # Base classes, common interfaces
Modules/
Orders/
{Company}.{Domain}.Orders.Api/ # Public contracts (DTOs, interfaces)
{Company}.{Domain}.Orders.Core/ # Domain + Application logic
{Company}.{Domain}.Orders.Infrastructure/ # Data access, external services
Inventory/
{Company}.{Domain}.Inventory.Api/
{Company}.{Domain}.Inventory.Core/
{Company}.{Domain}.Inventory.Infrastructure/
Shipping/
{Company}.{Domain}.Shipping.Api/
{Company}.{Domain}.Shipping.Core/
{Company}.{Domain}.Shipping.Infrastructure/
// Modules/Orders/Orders.Api/IOrderModule.cs
namespace {Company}.{Domain}.Orders.Api;
// Only this project is referenced by other modules
public interface IOrderModule
{
Task<OrderSummaryDto?> GetOrderSummaryAsync(
Guid orderId, CancellationToken ct);
Task<bool> OrderExistsAsync(Guid orderId, CancellationToken ct);
}
public sealed record OrderSummaryDto(
Guid Id, string CustomerName, decimal Total, string Status);
// Modules/Orders/Orders.Core/OrderModule.cs
namespace {Company}.{Domain}.Orders.Core;
internal sealed class OrderModule(OrderDbContext db) : IOrderModule
{
public async Task<OrderSummaryDto?> GetOrderSummaryAsync(
Guid orderId, CancellationToken ct)
{
return await db.Orders
.Where(o => o.Id == orderId)
.Select(o => new OrderSummaryDto(
o.Id, o.CustomerName, o.Total, o.Status.ToString()))
.FirstOrDefaultAsync(ct);
}
public async Task<bool> OrderExistsAsync(
Guid orderId, CancellationToken ct)
{
return await db.Orders.AnyAsync(o => o.Id == orderId, ct);
}
}
// Modules/Orders/Orders.Infrastructure/OrderDbContext.cs
namespace {Company}.{Domain}.Orders.Infrastructure;
internal sealed class OrderDbContext(
DbContextOptions<OrderDbContext> options) : DbContext(options)
{
public DbSet<Order> Orders => Set<Order>();
public DbSet<OrderItem> OrderItems => Set<OrderItem>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("orders"); // schema isolation
modelBuilder.ApplyConfigurationsFromAssembly(
typeof(OrderDbContext).Assembly);
}
}
// SharedKernel/Events/IIntegrationEvent.cs
public interface IIntegrationEvent : INotification
{
Guid EventId { get; }
DateTimeOffset OccurredAt { get; }
}
// Orders module publishes
public sealed record OrderPlacedIntegrationEvent(
Guid OrderId,
string CustomerName,
decimal Total) : IIntegrationEvent
{
public Guid EventId { get; } = Guid.NewGuid();
public DateTimeOffset OccurredAt { get; } = DateTimeOffset.UtcNow;
}
// Inventory module handles
internal sealed class ReserveInventoryOnOrderPlaced(
InventoryDbContext db)
: INotificationHandler<OrderPlacedIntegrationEvent>
{
public async Task Handle(
OrderPlacedIntegrationEvent notification,
CancellationToken ct)
{
// Reserve inventory for the order
var reservation = InventoryReservation.Create(
notification.OrderId, notification.OccurredAt);
db.Reservations.Add(reservation);
await db.SaveChangesAsync(ct);
}
}
// Each module has a registration extension
// Modules/Orders/Orders.Infrastructure/OrderModuleRegistration.cs
public static class OrderModuleRegistration
{
public static IServiceCollection AddOrderModule(
this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<OrderDbContext>(options =>
options.UseSqlServer(
configuration.GetConnectionString("Default"),
sql => sql.MigrationsHistoryTable(
"__EFMigrationsHistory", "orders")));
services.AddScoped<IOrderModule, OrderModule>();
services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssembly(
typeof(OrderModule).Assembly));
return services;
}
}
// WebApi/Program.cs
builder.Services
.AddOrderModule(builder.Configuration)
.AddInventoryModule(builder.Configuration)
.AddShippingModule(builder.Configuration);
// SharedKernel/BaseEntity.cs
public abstract class BaseEntity
{
public Guid Id { get; protected set; } = Guid.NewGuid();
private readonly List<IDomainEvent> _domainEvents = [];
public IReadOnlyList<IDomainEvent> DomainEvents =>
_domainEvents.AsReadOnly();
protected void AddDomainEvent(IDomainEvent e) => _domainEvents.Add(e);
public void ClearDomainEvents() => _domainEvents.Clear();
}
// SharedKernel/IUnitOfWork.cs
public interface IUnitOfWork
{
Task<int> SaveChangesAsync(CancellationToken ct = default);
}
InternalsVisibleTo for tests only)Modules/ folder structure in the solution| Question | Answer |
|---|---|
| How do modules communicate? | Integration events (async) or module interfaces (sync reads) |
| Can modules share a database? | Same server is OK, but use separate schemas |
| What goes in SharedKernel? | Base classes, common interfaces, integration event contracts |
| When to split into microservices? | When independent deployment or scaling is needed |
| How to handle transactions across modules? | Eventual consistency via events; avoid distributed transactions |