plugins/enterprise-architecture/skills/modular-architecture/SKILL.md
Module organization patterns including ports and adapters (hexagonal), module communication, and data isolation. Use when structuring modular monoliths, defining module boundaries, setting up inter-module communication, or isolating database contexts. Includes MediatR patterns for internal events.
npx skillsauth add melodic-software/claude-code-plugins modular-architectureInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill when you need to:
Keywords: modular monolith, modules, bounded contexts, ports and adapters, hexagonal architecture, module communication, data isolation, separate DbContext, MediatR, domain events, internal events, module boundaries
Organize code by modules (business capabilities), not layers. Each module is a self-contained vertical slice with its own:
src/
├── Modules/
│ ├── Ordering/
│ │ ├── Ordering.Core/ # Domain + Application
│ │ │ ├── Domain/ # Entities, Value Objects, Events
│ │ │ ├── Application/ # Commands, Queries, Handlers
│ │ │ └── Ports/ # Interfaces (driven/driving)
│ │ ├── Ordering.Infrastructure/ # External dependencies
│ │ │ ├── Persistence/ # EF Core, DbContext
│ │ │ └── Adapters/ # External service implementations
│ │ └── Ordering.DataTransfer/ # DTOs for module-to-module communication
│ ├── Inventory/
│ │ ├── Inventory.Core/
│ │ ├── Inventory.Infrastructure/
│ │ └── Inventory.DataTransfer/
│ └── Shared/ # Truly shared kernel (minimal)
│ └── Shared.Kernel/ # Common value objects, interfaces
└── Host/ # Composition root, startup
└── Api/ # Controllers, middleware
The hexagonal architecture separates business logic from external concerns through ports (interfaces) and adapters (implementations).
Detailed guide: See references/ports-adapters-guide.md
┌─────────────────────────────────────────────────────────────┐
│ DRIVING SIDE (Primary) │
│ Controllers, CLI, Message Handlers, Tests │
│ │ │
│ ┌──────▼──────┐ │
│ │ PORTS │ (Input interfaces) │
│ │ IOrderService│ │
│ └──────┬──────┘ │
│ │ │
│ ┌────────────▼────────────┐ │
│ │ APPLICATION │ │
│ │ (Use Cases/Handlers) │ │
│ └────────────┬────────────┘ │
│ │ │
│ ┌────────────▼────────────┐ │
│ │ DOMAIN │ │
│ │ (Entities, Value Objs) │ │
│ └────────────┬────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ PORTS │ (Output interfaces) │
│ │IOrderRepository│ │
│ └──────┬──────┘ │
│ │ │
│ DRIVEN SIDE (Secondary) │
│ Databases, External APIs, File Systems, Queues │
└─────────────────────────────────────────────────────────────┘
Driving Ports: Interfaces the application exposes (implemented by the application) Driven Ports: Interfaces the application needs (implemented by adapters)
Modules must communicate without creating tight coupling. Two primary patterns:
Detailed guide: See references/module-communication.md
For query operations where immediate response is needed:
// In Inventory module - needs to check product availability
public class CheckStockHandler
{
private readonly IOrderingModuleApi _orderingApi;
public async Task<StockStatus> Handle(CheckStockQuery query)
{
// Get order info through DataTransfer DTO
var orderDto = await _orderingApi.GetOrderSummary(query.OrderId);
// orderDto is from Ordering.DataTransfer project
}
}
For state changes that other modules need to react to:
// In Ordering module - publishes event after order is placed
public class PlaceOrderHandler
{
private readonly IMediator _mediator;
public async Task Handle(PlaceOrderCommand command)
{
// ... create order ...
// Publish integration event (handled by other modules)
await _mediator.Publish(new OrderPlacedIntegrationEvent(
order.Id, order.Items.Select(i => i.ProductId)));
}
}
// In Inventory module - handles the event
public class OrderPlacedHandler : INotificationHandler<OrderPlacedIntegrationEvent>
{
public async Task Handle(OrderPlacedIntegrationEvent notification, CancellationToken ct)
{
// Reserve inventory for the order
await _inventoryService.ReserveStock(notification.ProductIds);
}
}
Each module should own its data to prevent tight coupling at the database level.
Detailed guide: See references/data-patterns.md
// Ordering module's DbContext
public class OrderingDbContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderItems { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
// Only configure Ordering entities
builder.ApplyConfigurationsFromAssembly(typeof(OrderingDbContext).Assembly);
}
}
// Inventory module's DbContext
public class InventoryDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<StockLevel> StockLevels { get; set; }
}
MediatR provides the messaging infrastructure for both in-module CQRS and cross-module integration events.
Detailed guide: See references/mediatr-integration.md
// In each module's registration
public static class OrderingModule
{
public static IServiceCollection AddOrderingModule(this IServiceCollection services)
{
services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssembly(typeof(OrderingModule).Assembly));
services.AddScoped<IOrderingModuleApi, OrderingModuleApi>();
services.AddDbContext<OrderingDbContext>();
return services;
}
}
| Type | Scope | Use Case | | --- | --- | --- | | Domain Event | Within module | Aggregate state changes | | Integration Event | Cross-module | Notify other modules of changes |
This skill works with the event-storming skill for bounded context discovery:
Workflow:
Event Storming (discover "what")
↓
Bounded Contexts identified
↓
Modular Architecture (implement "where")
↓
Module structure created
↓
Fitness Functions (enforce boundaries)
Use the fitness-functions skill to enforce module boundaries:
When starting a new modular monolith:
references/ports-adapters-guide.md - Detailed hexagonal architecture patternsreferences/module-communication.md - Sync and async communication patternsreferences/data-patterns.md - Database isolation strategiesreferences/mediatr-integration.md - MediatR configuration and patternsDate: 2025-12-22 Model: claude-opus-4-5-20251101
development
Search Milan Jovanovic's .NET blog for Clean Architecture, DDD, CQRS, EF Core, and ASP.NET Core patterns. Use for finding applicable patterns, code examples, and architecture guidance. Invoke when working with .NET projects that could benefit from proven architectural patterns.
tools
Install and configure Data API Builder (DAB) for production SQL Server MCP access with RBAC
tools
Manage MssqlMcp servers - status, rebuild, and upstream updates
tools
Developer environment setup guides for Windows, macOS, Linux, and WSL. Use when setting up development machines, installing tools, configuring environments, or following platform-specific setup guides. Covers package management, shell/terminal, code editors, AI tooling, containerization, databases, and more.