Modulith (Modular Monolith)

Un único deploy, pero módulos internos con fronteras estrictas y comunicación explícita.

Contexto

Quieres la simplicidad del monolito y la autonomía modular de microservicios — sin la operativa distribuida.

Solución
  • Cada módulo (Sales, Billing, Shipping) es un bounded context.
  • Comunicación por interfaces públicas o eventos in-process.
  • Cada módulo es dueño de sus propias tablas; los demás no las leen.
  • Reglas de dependencia obligadas con análisis estático (NetArchTest, ArchUnitNET).

#Ejemplo en C#

CSHARP
src/
├── Modules/
│   ├── Sales/
│   │   ├── Sales.Public/      ← contratos hacia fuera
│   │   ├── Sales.Application/
│   │   ├── Sales.Domain/
│   │   └── Sales.Infrastructure/
│   ├── Billing/
│   │   └── …
│   └── Shipping/
│       └── …
└── Bootstrap/compone DI, expone HTTP
// Comunicación entre módulos: eventos in-process
public record OrderPlacedIntegrationEvent(Guid OrderId, decimal Total);

// Sales publica:
await _events.PublishAsync(new OrderPlacedIntegrationEvent(order.Id, order.Total));

// Billing escucha (mismo proceso, sin red):
public class CreateInvoiceOnOrderPlaced : INotificationHandler<OrderPlacedIntegrationEvent> { /* ... */ }

#Por qué importa

Si mañana necesitas extraer Billing a microservicio, basta con cambiar el bus in-process por uno real (RabbitMQ/Kafka). El refactor existe dentro del módulo.

Cuándo NO aplicarlo
  • Cuando ya tienes equipos completamente independientes con stacks distintos.
  • Cuando un módulo necesita escalar 100× más que el resto.
Tradeoffs
Pro Contra
Simplicidad operativa de monolito Disciplina de equipo necesaria
Camino claro hacia microservicios Tooling para enforcement de límites
Transacciones reales entre módulos (si lo permites) Tentación de saltarse las fronteras

#architecture #modulith