Arquitectura del Sistema
Monolith, Modular Monolith, Microservices y SOA: cómo dividir y desplegar un sistema, con sus trade-offs reales en deployment, boundaries, escalabilidad y modelo de fallos — no una jerarquía donde una opción sea siempre mejor.
La Arquitectura del Sistema cambia de escala. DDD nos ayuda a entender:
¿Cómo dividir conceptualmente el dominio?
Ahora preguntamos:
¿Cómo dividimos y desplegamos el sistema?
Las opciones principales son Monolith, Modular Monolith, Microservices y SOA.
Monolith
Un monolith tiene un único deployment unit principal:
flowchart TD
App["Application"] --> Pay["Payments"]
App --> Ord["Orders"]
App --> Inv["Inventory"]
Pay --> DB[("Database")]
Ord --> DB
Inv --> DBEs importante separar dos ideas que suelen confundirse: Monolith ≠ Bad Architecture. Un monolith puede estar muy bien diseñado internamente. También puede escalar horizontalmente:
flowchart TD
LB["Load Balancer"] --> A1["App"]
LB --> A2["App"]
LB --> A3["App"]
A1 --> DB[("Database")]
A2 --> DB
A3 --> DBVentajas
- deployment simple;
- debugging más sencillo;
- menor latencia interna;
- transacciones locales;
- menor complejidad operativa.
Desventajas
- todos los módulos comparten deployment;
- scaling menos granular;
- los boundaries internos pueden degradarse;
- cambios en un módulo pueden afectar todo el deployment.
Modular Monolith
Mantiene un deployment, pero con boundaries internos claros:
flowchart TD App["Application"] --> Pay["Payments"] App --> Ord["Orders"] App --> Inv["Inventory"] App --> Cus["Customers"]
Los módulos tienen una API interna explícita. La diferencia entre un buen y un mal modular monolith suele estar en esto:
flowchart LR
subgraph OK["Correcto"]
O1["Orders"] --> P1["Payments API"]
end
subgraph BAD["Incorrecto"]
O2["Orders"] --> P2["payments.internal.repository"]
endEl objetivo
Obtener alta modularidad, deployment sencillo y transacciones locales, sin pagar todavía el costo de la red, el estado distribuido y los fallos distribuidos.
Cuándo usarlo
Es especialmente apropiado cuando:
- la aplicación es compleja;
- hay dominios bien definidos;
- el equipo todavía es relativamente pequeño;
- no existe una necesidad clara de deployment independiente;
- la distribución agregaría complejidad sin resolver un problema concreto.
Cuándo puede quedarse corto
Puede resultar insuficiente cuando:
- determinados componentes necesitan escalar independientemente;
- distintas partes tienen ciclos de deployment muy diferentes;
- los equipos necesitan ownership y deployment autónomos;
- hay razones de peso para aislar componentes;
- existe una necesidad real de distribución.
Microservices
Una arquitectura de microservices distribuye el sistema en varios servicios autónomos:
flowchart TD
GW["API Gateway"] --> PS["Payment Service"]
GW --> OS["Order Service"]
GW --> IS["Inventory Service"]
PS --> PDB[("Payment DB")]
OS --> ODB[("Order DB")]
IS --> IDB[("Inventory DB")]Un servicio debería representar una business capability suficientemente cohesionada y tener un boundary claro. Microsoft recomienda utilizar domain analysis y bounded contexts como base para definir estos límites.
Ventajas
- despliegue independiente
- escalabilidad independiente
- ownership
- aislamiento potencial de fallos
- autonomía tecnológica
Costos
La distribución introduce toda una nueva categoría de problemas: fallos de red, timeouts, reintentos, fallos parciales, distributed tracing, consistencia eventual, descubrimiento de servicios, sincronización de datos y complejidad de deployment.
Adoptar microservicios es también decidir asumir la complejidad de un sistema distribuido.
Un mal microservicio
No todo servicio pequeño es un buen microservicio:
flowchart LR CS["Checkout Service"] --> PS["Payment Service"] PS --> PPS["Payment Provider Service"] PPS --> CuS["Currency Service"] CuS --> DS["Database Service"]
Si una única operación simple requiere ocho network hops, probablemente la granularidad sea incorrecta. Microsoft también advierte sobre servicios excesivamente granulares, porque pueden aumentar la complejidad y degradar la performance.
SOA
Service-Oriented Architecture es una familia de arquitecturas orientadas a servicios, especialmente relevante en contextos enterprise. Un esquema clásico:
flowchart TD Cl["Clients"] --> ESB["ESB"] ESB --> PS["Payment Service"] ESB --> CS["Customer Service"] ESB --> OS["Order Service"] ESB --> ERP["ERP"]
Históricamente, SOA estuvo asociado a la integración empresarial, la gobernanza centralizada, los ESB, la reutilización de servicios, la orquestación y la integración de sistemas heterogéneos.
Microservicios comparte algunos principios, pero suele enfatizar más la autonomía, el despliegue independiente, la responsabilidad descentralizada y los bounded contexts. No existe una frontera universalmente aceptada entre ambos.
Monolith vs. Modular Monolith vs. Microservices
| Monolith | Modular Monolith | Microservices | |
|---|---|---|---|
| Deployment | Uno | Uno | Múltiples |
| Boundaries | Pueden ser débiles | Fuertes | Distribuidos |
| Network | No entre módulos | No entre módulos | Sí |
| ACID local | Sencillo | Sencillo | Limitado entre services |
| Scaling | Global | Global | Independiente |
| Operaciones | Simples | Simples/moderadas | Complejas |
| Modelo de fallos | Principalmente local | Principalmente local | Distribuido |
No existe una jerarquía donde “Monolith → peor” y “Microservices → mejor”. Existe un conjunto diferente de trade-offs. Fowler destaca precisamente el costo adicional —el llamado Microservice Premium— asociado a operar una arquitectura distribuida.
Herramientas y tecnologías
Microservicios suele combinarse con Docker, Kubernetes, AWS ECS/EKS, Azure Kubernetes Service, service mesh, Kafka, REST/gRPC y distributed tracing. Pero ninguna de estas tecnologías convierte automáticamente una aplicación en una buena arquitectura de microservicios.
Referencias
- Martin Fowler, Microservices.
- Martin Fowler, Monolith First.
- Microsoft Azure Architecture Center, Microservices Architecture Style.
