ワンクリックで
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 |