Modular Monolith
Deployment común y límites explícitos
Modular Monolith combina una frontera de deployment compartida con módulos de responsabilidades claras, contratos deliberados y dependencias controladas. Es un monolith cuya estructura interna se protege explícitamente. La modularidad no aparece por cambiar los nombres de carpetas ni por adoptar un framework.
Grzybek caracteriza módulos con interfaces definidas y encapsulación, y rechaza identificar monolito con falta de diseño. Modular Monolith: A Primer. Brown distingue organización del código y encapsulación efectiva: agrupar tipos no impide que otros dependan de sus detalles si siguen accesibles. Modular monolith and package by component.
modularity desarrolla encapsulación, decisiones ocultas e interacción controlada como propiedades que este perfil busca sostener; el nombre del perfil no demuestra esas propiedades. architecture-profile sitúa este perfil junto a alternativas con distintas fronteras operativas, sin ordenar unas como versiones superiores de otras.
Diferencia respecto de Monolith
Monolith caracteriza principalmente una frontera arquitectónica o de deployment amplia. Modular Monolith añade una disciplina de estructura interna. No son categorías incompatibles: un monolito bien modularizado pertenece a ambas descripciones.
Single deployable
├── Orders
├── Payments
├── Inventory
└── CustomersEl diagrama expresa intención, no evidencia. Orders no debería leer o modificar arbitrariamente la representación privada de Payments; debería usar un contrato que preserve responsabilidades e invariantes. Ese contrato puede ser una llamada local y no necesita simular una API de red.
Los módulos tampoco deben comportarse exactamente como microservicios dentro del proceso. Introducir serialización, mensajería persistente o consistencia eventual para toda interacción puede perder ventajas locales sin conseguir independencia de deployment.
Contratos y dependencia
Cada módulo declara qué ofrece, qué consume y qué oculta. Su contrato expresa operaciones, tipos públicos o eventos internos; la implementación puede cambiar mientras preserve expectativas acordadas. Ownership implica autoridad sobre reglas y estado, además de una persona responsable de revisar cambios.
Las dependencias necesitan dirección y mecanismos de protección. Visibilidad del lenguaje, separación de proyectos y revisiones arquitectónicas pueden impedir accesos accidentales. Spring Modulith ilustra verificaciones de ciclos y referencias a componentes internos; es un ejemplo de implementación, no una dependencia del perfil. Verifying Application Module Structure.
Un contrato que expone toda la representación interna debilita el límite aunque las referencias pasen por una interfaz. Un módulo que debe cambiar siempre junto a otros revela acoplamiento que conviene analizar. La independencia es relativa al escenario de cambio, no ausencia total de colaboración.
Ejes del perfil
| Eje | Compromiso y límite habitual |
|---|---|
| Decomposition | Módulos con responsabilidades deliberadas dentro de una aplicación. |
| Dependency Structure | Contratos públicos y acceso restringido a implementación. |
| Communication | Colaboración local frecuente; eventos internos son una opción. |
| Coordination | Puede usar transacciones locales, con acuerdos entre módulos. |
| Data Ownership | Responsabilidad lógica por datos aunque comparta almacenamiento físico. |
| Isolation | Encapsulación de cambios; no garantiza aislamiento de recursos o fallos. |
| Tenancy | Decisión adicional sobre contexto, autorización y partición de datos. |
| Deployment | Artefacto o release común; módulos normalmente no desplegables por separado. |
| Execution Model | Peticiones y tareas locales; integraciones externas siguen siendo remotas. |
| Scaling | Réplicas de la unidad, con límites compartidos que deben medirse. |
El perfil combina decisiones de estructura, delivery y coordinación. Un módulo autónomo conceptualmente no implica que pueda sustituirse durante ejecución ni que todas sus dependencias estén ausentes.
Data ownership y transacciones
data-ownership distingue autoridad lógica y ubicación física: compartir una base de datos no obliga a compartir autoridad sobre todas las tablas. Un módulo puede controlar sus datos, publicar operaciones y ocultar detalles a otros. Permisos, interfaces de acceso y convenciones revisadas deben ajustarse al lenguaje y al almacenamiento elegido.
Una operación entre módulos puede aprovechar una transacción local cuando usa recursos compatibles. La frontera de la transacción debe ser deliberada: quién inicia, qué invariantes protege y qué ocurre si falla un participante. La atomicidad no justifica saltarse contratos para modificar datos ajenos.
También puede elegirse interacción asincrónica cuando el dominio lo permite. Esa decisión añade manejo de publicación y repetición. transactional-outbox explica cómo preservar la relación entre estado y mensaje; no es una exigencia para todas las colaboraciones del monolito.
Pruebas, delivery y observabilidad
Los tests de módulo pueden ejercitar contratos con dependencias controladas. Los tests integrados verifican colaboraciones reales y las reglas estructurales detectan accesos que el equipo decidió prohibir. Ninguna técnica reemplaza revisar si los límites siguen representando el negocio.
El release compartido simplifica inventario de artefactos y entornos, pero obliga a mantener compatibilidad de cambios concurrentes. Un cambio local puede necesitar entregar el conjunto aunque solo un módulo se haya modificado. La coordinación puede gestionarse sin convertir cada entrega en una reunión de todos los equipos.
Logs y métricas por módulo ayudan a observar saturación y errores. Un fallo de memoria o un recurso común puede afectar varias capacidades. bulkhead permite estudiar límites concretos; separar carpetas no crea límites de CPU, conexiones ni threads.
operability permite contrastar menos unidades operativas con diagnóstico e intervención dentro del runtime compartido. Atribuir recursos y contener impacto exige fronteras efectivas; tener un único despliegue no demuestra facilidad operacional para cada módulo.
Ejemplo: reservas y cobros
Supongamos Orders responsable de aceptar solicitudes y Payments responsable del estado de cobro. Orders pide una operación a Payments mediante un contrato; no interpreta sus tablas privadas. Ambos participan en una entrega común y pueden usar una transacción local si el caso y la implementación lo permiten.
Agregar un nuevo medio de pago debería afectar principalmente Payments y sus contratos cuando sea necesario. Si cada cambio obliga a modificar todos los módulos, el dibujo no demuestra modularidad. Si la operación llama a un proveedor externo, timeout y fallos parciales siguen existiendo aunque la coordinación interna sea local.
El análisis busca preservar invariantes y hacer explícita la propagación del cambio. No impone que cada módulo tenga un servidor, repositorio Git, pipeline o base de datos separados.
Equipos, costos y límites
El ownership de módulos puede distribuir trabajo entre equipos manteniendo un entorno común. La autonomía de diseño puede ser considerable, mientras deployment, runtime y algunas decisiones tecnológicas permanecen compartidos. Confundir esas formas de autonomía lleva a prometer independencia que el perfil no ofrece. modifiability puede mostrar cambios locales con buena encapsulación; deployability evalúa separadamente la entrega compartida, que puede satisfacer un escenario sin ofrecer deployment por módulo. quality-attribute-trade-offs compara ese paquete con alternativas según escenarios, sin declarar superioridad universal.
El costo principal incluye sostener contratos, reglas de dependencia y claridad de responsabilidades bajo presión de entrega. Una abstracción común demasiado amplia puede volver a acoplar módulos. La disciplina debe responder a escenarios reales, no a anticipar todas las extensiones posibles.
architectural-quantum [ADVANCED] permite describir cómo varios módulos bien encapsulados participan en un alcance operacional amplio con deployment común. Mantener ese quantum no invalida la modularidad interna ni convierte extracción en objetivo obligatorio.
Separar módulos facilita examinar una extracción futura, pero no la garantiza. Un contrato local puede depender de transacciones, objetos compartidos o tiempos de respuesta que no sobreviven a una llamada remota. La extracción necesita un nuevo análisis de arquitectura.
cost-efficiency permite comparar menor topología operacional con una unidad de scaling compartida. Si un hotspot obliga a replicar todo el deployment, el ahorro de infraestructura común puede cambiar. Ni menos procesos ni scaling por módulo prometido sin mecanismos demuestran menor costo total.
Composición y elección
Layered, Hexagonal o Vertical Slice pueden organizar el interior de los módulos. event-driven-architecture puede organizar interacciones mediante hechos dentro del mismo proceso y deployment. No convierte al perfil en servicios independientes ni exige que todos los módulos se comuniquen mediante eventos. Una realización híbrida puede conservar el núcleo modular y delegar jobs o handlers de integración a serverless-architecture. Esa composición debe describir qué sigue compartiendo deployment y qué capacidades operan fuera.
Modular Monolith no implica un sistema distribuido por separar módulos. La aplicación puede, por supuesto, usar infraestructura distribuida y sistemas externos. Tampoco es una estructura de carpetas. Sus límites deben existir en contratos, código, datos y ownership.
Puede ser la arquitectura final adecuada. service-based-architecture y microservices ofrecen otros compromisos de entrega y operación, no etapas obligatorias. architectural-drivers, architectural-significance y architecture-profiles ayudan a evaluar cuándo la independencia operativa aporta un beneficio que justifica sus costos.