← Volver al Blog
Arquitectura15 min5 sept 2026

Monolito modular: la arquitectura que conviene probar antes de microservicios.

Aprende cómo un monolito modular usa límites de dominio, APIs internas y propiedad de datos para controlar el acoplamiento, y cuándo tiene sentido extraer un módulo como microservicio.

Monolito modular: la arquitectura que conviene probar antes de microservicios.

Monolito modular: la arquitectura que conviene probar antes de microservicios#

Los microservicios pueden resolver problemas arquitectónicos reales.

También pueden introducir problemas que todavía no tienes.

Un backend empieza siendo relativamente sencillo. La aplicación crece. Las dependencias se vuelven más difíciles de seguir. Un cambio en orders rompe inesperadamente payments. Diferentes partes del código acceden a las mismas tablas. Cada despliegue empieza a sentirse más arriesgado de lo necesario.

El diagnóstico suele ser correcto:

el sistema tiene demasiado acoplamiento.

Pero la solución propuesta muchas veces da un salto bastante mayor:

“Deberíamos dividir todo en microservicios.”

Ese salto no siempre es necesario.

Antes de convertir llamadas locales en llamadas de red, consistencia local en workflows entre servicios y un despliegue en muchos componentes operados de forma independiente, existe otra arquitectura que merece atención:

el monolito modular.

Un monolito modular mantiene una sola unidad de despliegue, pero establece límites explícitos entre las capacidades de negocio que viven dentro de ella.

La distinción es importante.

A menudo, el primer problema que necesitamos resolver no es la distribución.

Es la modularidad.

Y si algunos módulos terminan necesitando escalado, despliegue, ownership o aislamiento de fallos independientes, esos límites internos pueden hacer que extraerlos sea considerablemente más seguro.

Qué es un monolito modular#

Un monolito modular es una aplicación desplegable como una sola unidad, pero dividida internamente en módulos claramente separados.

La palabra importante no es monolito.

Es modular.

Imaginemos un backend de comercio electrónico con tres capacidades:

Usuarios
Pedidos
Pagos

Un monolito tradicional puede tener carpetas con esos nombres y, aun así, permitir que prácticamente cualquier parte del sistema acceda a cualquier repositorio, modelo o tabla.

arquitectura de monolito tradicional

Un monolito modular va más allá.

Cada módulo tiene una frontera deliberada:

Usuarios
 ├── API pública
 ├── lógica de dominio
 ├── implementación interna
 └── datos propios

Pedidos
 ├── API pública
 ├── lógica de dominio
 ├── implementación interna
 └── datos propios

Pagos
 ├── API pública
 ├── lógica de dominio
 ├── implementación interna
 └── datos propios
arquitectura de monolito modular

Los demás módulos deberían comunicarse mediante contratos explícitos en lugar de importar directamente repositorios, objetos internos o código de persistencia.

El resultado sigue siendo:

una aplicación y una unidad de despliegue.

Pero sus responsabilidades internas están mucho mejor aisladas.

Esta es la primera idea que conviene conservar:

la topología de despliegue y la modularidad del código son decisiones distintas.

No necesitamos una frontera de red para crear una buena frontera arquitectónica.

Monolito tradicional vs monolito modular vs microservicios#

Cada arquitectura hace trade-offs diferentes.

| Característica | Monolito tradicional | Monolito modular | Microservicios | | ------------------------------- | ------------------------- | --------------------------------- | -------------------------------------------------------------------------- | | Despliegue | Una unidad | Una unidad | Múltiples unidades independientes | | Límites internos | Frecuentemente débiles | Explícitos y controlados | Fronteras de servicio/runtime | | Comunicación | Llamadas directas | APIs/eventos internos controlados | Red/mensajería | | Propiedad de datos | Frecuentemente compartida | Definida por módulo | Definida por servicio | | Transacciones | Normalmente locales | Normalmente locales | Locales por servicio; los workflows entre servicios requieren coordinación | | Debugging | Centralizado | Principalmente centralizado | Distribuido | | Escalado independiente | No | No | Sí | | Despliegue independiente | No | No | Sí | | Fallos de red entre capacidades | No | No | Sí | | Complejidad operativa | Baja | Baja a moderada | Mayor | | Refactor entre fronteras | Relativamente barato | Relativamente barato | Más costoso |

arquitectura de microservicios

Por eso un monolito modular no es simplemente una versión pequeña de microservicios.

La decisión es otra:

aislamiento lógico antes de distribución física.

El problema real suele ser el acoplamiento#

Cuando un desarrollador dice:

“Nuestro monolito es imposible de mantener.”

a menudo la palabra monolito está recibiendo más culpa de la que merece.

Los síntomas reales suelen parecerse a esto:

orders -> repositorio de users
orders -> tablas de payments
payments -> entidades de orders
users -> internals de notifications
notifications -> persistencia de orders

Con el tiempo, el grafo de dependencias deja de ser fácil de razonar.

Una frontera de módulo útil debería convertir algunas dependencias en válidas y otras en explícitamente prohibidas.

dependencias legales e ilegales entre módulos

Separar esos mismos componentes en procesos independientes no corrige automáticamente el diseño.

Podemos terminar con:

orders-service
      ↓
users-service
      ↓
payments-service
      ↓
orders-service
      ↓
notification-service

El acoplamiento sigue ahí.

Solo que ahora cada dependencia también puede fallar por red.

Distribuir hace que las fronteras sean más costosas.

No las hace automáticamente mejores.

El coste de los microservicios#

Los microservicios proporcionan capacidades que un monolito no ofrece con la misma facilidad:

  • despliegue independiente;
  • escalado independiente;
  • mayor aislamiento en runtime;
  • ownership operativo autónomo;
  • decisiones de infraestructura distintas por servicio.

Esas capacidades pueden ser muy valiosas cuando realmente se necesitan.

Pero tienen un coste.

Una llamada local como:

payment = payments.authorize(order)

puede convertirse en:

Orders Service
      │
      │ HTTP / gRPC
      ▼
Payments Service

Y con esa frontera aparecen preguntas nuevas.

¿Qué ocurre si Payments no responde?

¿Cuánto debería esperar Orders?

¿Debemos reintentar?

¿La operación es idempotente?

¿Qué pasa si Payments completa la transacción pero Orders pierde la respuesta?

¿Cómo seguimos el request entre servicios?

¿Cómo autenticamos la comunicación interna?

¿Cómo desplegamos cambios incompatibles en las APIs?

¿Cómo mantenemos consistencia cuando varios servicios participan en un workflow?

¿Cómo probamos el flujo completo?

No son argumentos contra los microservicios.

Son consecuencias de un sistema distribuido.

Martin Fowler ha descrito precisamente costes como la comunicación remota, la consistencia eventual y la complejidad operativa. La documentación de AWS también trata la latencia, la consistencia de datos y la coordinación operacional como problemas que deben diseñarse explícitamente al separar workloads en servicios.

Si la organización todavía no necesita las ventajas de la distribución, asumir esas cargas demasiado pronto puede convertirse en sobreingeniería arquitectónica.

La Ley de Conway también importa#

La arquitectura no es solamente una cuestión de código.

La estructura del equipo importa.

La Ley de Conway describe la tendencia de los sistemas a reflejar las estructuras de comunicación de las organizaciones que los construyen.

Pensemos en una empresa con varios equipos autónomos.

Cada equipo necesita:

construir
probar
desplegar
escalar
operar

una capacidad de negocio sin coordinar cada release con todos los demás.

Los servicios independientes pueden reforzar esa estructura organizativa.

Ahora pensemos en un equipo de cinco desarrolladores backend operando dieciséis microservicios.

Esos mismos cinco desarrolladores pueden terminar manteniendo:

múltiples pipelines
configuración de runtime
service discovery
distributed tracing
mensajería
contratos de APIs
seguridad de red
testing entre servicios
varios sistemas de persistencia

Las fronteras de servicio no crearon dieciséis equipos autónomos.

Crearon más superficies operativas para el mismo equipo.

Los microservicios resultan especialmente útiles cuando la independencia organizativa y la independencia del runtime se refuerzan entre sí.

Antes de ese punto, buenos módulos internos pueden resolver el problema inmediato con un coste mucho menor.

Cómo estructurar un monolito modular#

Crear una carpeta llamada modules/ no convierte una aplicación en modular.

La arquitectura aparece cuando existen restricciones.

1. Organiza por capacidades de negocio#

Evita que la estructura superior sea únicamente técnica:

controllers/
services/
repositories/
models/

Ese diseño organiza el código por responsabilidad de implementación.

Una sola funcionalidad puede obligarnos a movernos entre múltiples carpetas.

Prefiere límites orientados al negocio:

modules/
├── users/
├── catalog/
├── orders/
├── payments/
└── shipping/

Cada módulo puede mantener sus propias capas internas:

modules/
└── orders/
    ├── api/
    ├── application/
    ├── domain/
    ├── infrastructure/
    └── tests/

Ahora la mayor parte de los cambios relacionados con pedidos ocurre dentro de orders.

Domain-Driven Design puede ayudar a descubrir estos límites.

Un bounded context no es automáticamente un módulo ni un microservicio. Define el límite dentro del cual un determinado modelo de dominio tiene sentido.

Eso lo convierte en una entrada útil para descubrir módulos.

Un bounded context bien identificado puede primero materializarse como módulo interno.

Que más adelante merezca convertirse en servicio es otra decisión.

2. Dale a cada módulo una API pública#

Supongamos que Orders necesita autorizar un pago.

Esto crea acoplamiento innecesario:

from modules.payments.infrastructure.repositories import PaymentRepository

Orders ahora conoce un detalle interno de Payments.

Es preferible depender de un contrato intencional:

from modules.payments.api import PaymentService

Por ejemplo:

class PaymentService:
    def authorize(
        self,
        customer_id: str,
        amount: Decimal,
    ) -> PaymentResult:
        ...

Orders conoce la capacidad que necesita.

No necesita saber:

qué tablas utiliza Payments
qué ORM utiliza
qué proveedor procesa el pago
qué entidades internas mantiene

Eso es encapsulación.

Herramientas como Spring Modulith siguen una filosofía similar: distinguen APIs públicas, componentes internos y eventos de aplicación, y permiten verificar relaciones estructurales entre módulos.

3. Establece propiedad explícita de los datos#

Una regla fuerte que podemos aplicar a este diseño es:

solo el módulo propietario debería acceder directamente a su implementación de persistencia.

Si Orders es propietario de los pedidos, Payments no debería consultar directamente:

SELECT *
FROM orders
WHERE id = ?;

Y Orders tampoco debería entrar en:

SELECT *
FROM payment_transactions
WHERE order_id = ?;

Aunque todos utilicen la misma instancia de PostgreSQL, la propiedad lógica puede mantenerse:

Database
│
├── users schema
│   └── propiedad de Users
│
├── orders schema
│   └── propiedad de Orders
│
└── payments schema
    └── propiedad de Payments

No es obligatorio tener un servidor de base de datos por módulo.

Lo importante es el ownership.

Los demás módulos deberían obtener la información mediante el contrato público del módulo propietario o reaccionando a los eventos que publique.

Esta disciplina importa porque el acceso compartido a tablas crea algunas de las dependencias más difíciles de eliminar posteriormente.

Si Payments termina convirtiéndose en servicio, separar sus datos será mucho menos traumático si ningún otro módulo dependía directamente de sus tablas.

4. Usa llamadas síncronas cuando necesitas una respuesta inmediata#

No toda interacción necesita un evento.

Si Orders no puede continuar hasta saber si el pago fue autorizado, una llamada síncrona entre módulos es razonable:

result = payment_service.authorize(
    customer_id=customer.id,
    amount=order.total,
)

if not result.approved:
    raise PaymentRejected()

La regla arquitectónica no es “evita llamadas síncronas”.

Es:

depende del contrato público, no de la implementación.

5. Usa eventos cuando los consumidores no deberían convertirse en dependencias directas#

Ahora supongamos que un pedido fue creado correctamente.

Pueden interesarse varios módulos:

Inventory
Analytics
Notifications
Shipping

Orders no necesariamente necesita conocerlos uno por uno.

Puede publicar:

OrderPlaced(
    order_id=order.id,
    customer_id=order.customer_id,
)

y permitir que distintos consumidores reaccionen:

               ┌──────────────► Inventory
               │
Orders ──► OrderPlaced ───────► Analytics
               │
               ├──────────────► Notifications
               │
               └──────────────► Shipping

La aplicación sigue ejecutándose en un solo proceso.

No hace falta introducir Kafka únicamente para preservar límites entre módulos.

Pero hay una distinción importante:

un evento no es automáticamente asíncrono ni durable.

Un bus de eventos in-process puede ejecutar sus handlers de forma síncrona.

Y si el negocio necesita entrega garantizada, hará falta una estrategia de persistencia apropiada, como un mecanismo durable de publicación o un transactional outbox.

Desacoplamiento y mensajería confiable son problemas distintos.

6. Haz cumplir la encapsulación con el lenguaje y el sistema de build#

Los diagramas no impiden imports incorrectos.

El código puede hacerlo.

Java dispone de visibilidad de paquetes y mecanismos de modularización.

Go tiene packages y internal.

Rust ofrece controles explícitos de visibilidad.

Python depende más de convenciones y APIs de paquete, por lo que los tests de arquitectura y reglas de importación pueden ser particularmente útiles.

Por ejemplo:

payments/
├── __init__.py
├── api.py
└── _internal/
    ├── models.py
    ├── repository.py
    └── stripe_gateway.py

Otros módulos pueden utilizar:

from payments.api import PaymentService

pero una validación arquitectónica debería rechazar:

from payments._internal.repository import PaymentRepository

La idea es simple:

haz que la dependencia correcta sea más fácil que la incorrecta.

7. Prueba la arquitectura#

Los límites entre módulos son requisitos arquitectónicos.

Trátalos como requisitos ejecutables.

CI debería poder detectar reglas como:

Orders no puede importar internals de Payments.

Payments no puede depender de Shipping.

Notifications puede consumir eventos de Orders,
pero no acceder a su persistencia.

Users no puede depender de Payments.

Sin enforcement, las excepciones se acumulan.

Alguien terminará diciendo:

“Solo voy a llamar al repositorio directamente esta vez.”

Meses después, la excepción se habrá convertido en la arquitectura.

Herramientas como Spring Modulith y librerías de architecture testing permiten convertir este tipo de reglas en restricciones verificables.

Una estructura práctica#

Un backend de comercio electrónico podría verse así:

src/
│
├── app/
│   ├── bootstrap.py
│   └── event_bus.py
│
├── modules/
│   ├── users/
│   │   ├── api.py
│   │   ├── domain/
│   │   ├── application/
│   │   ├── infrastructure/
│   │   └── tests/
│   │
│   ├── orders/
│   │   ├── api.py
│   │   ├── events.py
│   │   ├── domain/
│   │   ├── application/
│   │   ├── infrastructure/
│   │   └── tests/
│   │
│   ├── payments/
│   │   ├── api.py
│   │   ├── events.py
│   │   ├── domain/
│   │   ├── application/
│   │   ├── infrastructure/
│   │   └── tests/
│   │
│   └── shipping/
│       ├── api.py
│       ├── domain/
│       ├── application/
│       ├── infrastructure/
│       └── tests/
│
└── main.py

Fíjate en lo que no ocupa el centro del diseño:

global_models/
global_repositories/
shared_business_logic/
utils_everything/

Compartir infraestructura técnica puede ser razonable.

Compartir modelos de negocio mutables es mucho más peligroso.

Qué debería compartirse realmente#

Hay conceptos técnicos que pueden compartirse sin destruir las fronteras:

logging
telemetría
configuración
bootstrap de base de datos
abstracciones de reloj
infraestructura base de eventos

Conviene desconfiar más de:

shared/entities/
shared/business/
shared/services/
common/domain/

Estas carpetas suelen convertirse en atajos alrededor de los límites del dominio.

Orders y Payments pueden utilizar el concepto Customer y aun así necesitar representaciones diferentes.

Eso no necesariamente es una mala duplicación.

Diferentes bounded contexts pueden modelar de manera distinta un mismo concepto porque resuelven problemas diferentes.

Forzar un único modelo universal para toda la aplicación puede aumentar el acoplamiento en lugar de reducirlo.

Un monolito modular no significa una transacción gigante#

Tener un solo deployment no implica que cada workflow deba ejecutarse dentro de una enorme transacción de base de datos.

Un caso de uso que realmente necesite atomicidad puede utilizar una transacción local.

Otros procesos pueden modelarse como varios pasos.

La ventaja es que esas fronteras pueden introducirse deliberadamente.

Puedes conservar consistencia local donde tenga sentido e introducir workflows asíncronos o eventualmente consistentes únicamente cuando el dominio lo justifique.

No añadas semánticas distribuidas solamente para imitar microservicios dentro de un proceso.

Cuándo debería un módulo convertirse en microservicio#

“El proyecto está creciendo” no es una razón suficientemente precisa.

Una regla más útil es:

extrae un módulo cuando la independencia física resuelva un problema demostrado.

Escalado independiente#

Una capacidad desarrolla un perfil de recursos radicalmente distinto.

El procesamiento de imágenes, por ejemplo, puede consumir mucha más CPU que el resto de la aplicación.

Escalar todo el monolito únicamente por ese workload puede ser ineficiente.

Despliegue independiente#

Un dominio pertenece a un equipo autónomo que necesita desplegar a su propio ritmo sin coordinar cada release del sistema.

Aislamiento de fallos#

Una capacidad necesita características de disponibilidad que justifican aislarla del resto del proceso.

Requisitos distintos de runtime o infraestructura#

Un workload obtiene un beneficio real de otro runtime, tecnología de almacenamiento o modelo de infraestructura.

Autonomía organizativa#

Varios equipos necesitan ownership operativo independiente sobre distintas capacidades del negocio.

Seguridad o cumplimiento#

Un dominio necesita controles de infraestructura o acceso más estrictos que el resto de la aplicación.

Estas son razones concretas.

“Netflix usa microservicios” no lo es.

Cómo puede evolucionar un monolito modular hacia microservicios#

Supongamos que Payments termina necesitando ownership y escalado independientes.

Hoy:

┌─────────────────────────────┐
│         Aplicación          │
│                             │
│  ┌────────┐    API    ┌──────────┐
│  │ Orders │ ────────► │ Payments │
│  └────────┘           └──────────┘
│                             │
│                        datos de Payments
└─────────────────────────────┘

Más adelante:

┌───────────────┐      HTTP/gRPC/Event      ┌──────────────────┐
│ Orders Module │ ─────────────────────────► │ Payments Service │
└───────────────┘                            └──────────────────┘
                                                     │
                                              Payments database
extracción de un módulo hacia un microservicio

Uno de los grandes cambios arquitectónicos es que una frontera lógica se convierte en frontera de transporte y despliegue.

Pero la extracción no es trivial.

Todavía tendrás que resolver:

fallos de red
autenticación
timeouts
retries
idempotencia
observabilidad
compatibilidad de APIs
migración de datos
deployment
recuperación ante fallos
workflows entre servicios

El monolito modular no elimina esos problemas.

Los pospone hasta que aportan valor.

Y evita tener que descubrir la frontera de negocio al mismo tiempo que el equipo introduce toda la complejidad de un sistema distribuido.

Eso reduce significativamente el riesgo de la migración.

Una estrategia de migración más segura#

En lugar de:

Big Ball of Mud
        │
        ▼
Separar todo
        │
        ▼
20 microservicios

es preferible una evolución como:

Big Ball of Mud
        │
        ▼
Identificar límites de dominio
        │
        ▼
Monolito modular
        │
        ▼
Aplicar APIs entre módulos
        │
        ▼
Establecer propiedad de datos
        │
        ▼
Observar problemas reales
        │
        ▼
Extraer módulos justificados
        │
        ▼
Arquitectura híbrida

Es un enfoque evolutivo.

Las decisiones costosas se retrasan hasta disponer de más información sobre el dominio, el workload y la organización.

No todos los módulos tienen que convertirse en servicios.

Un sistema puede permanecer como monolito modular durante toda su vida útil.

También puede terminar en una arquitectura híbrida donde solo algunas capacidades se despliegan de manera independiente.

Ambos pueden ser resultados correctos.

Errores comunes en un monolito modular#

Los módulos solo son carpetas#

Si todos pueden importar internals de todos, la frontera es cosmética.

Todos los módulos consultan todas las tablas#

Entonces la base de datos se ha convertido en el verdadero grafo de dependencias.

Define ownership.

shared/ se convierte en un nuevo monolito#

El código de negocio compartido puede destruir silenciosamente el aislamiento.

Mantén lo común pequeño y principalmente técnico.

Todo utiliza eventos#

Los eventos son útiles, pero abusar de ellos puede volver difíciles de seguir workflows sencillos.

Usa llamadas síncronas cuando sus semánticas sean adecuadas.

Nada utiliza eventos#

El extremo opuesto crea largas cadenas de dependencias directas.

Usa eventos cuando varios consumidores deban reaccionar sin convertirse en dependencias explícitas del productor.

Los módulos siguen capas técnicas#

controllers, repositories y services describen responsabilidades de implementación.

Los módulos de nivel superior deberían representar normalmente capacidades de negocio.

La arquitectura solo existe en documentación#

Un diagrama no bloquea una dependencia incorrecta.

Automatiza las reglas importantes.

El equipo diseña servicios imaginarios del futuro#

No crees veinte módulos artificiales porque algún día podrían existir veinte microservicios.

Encuentra límites cohesivos para el sistema que tienes hoy.

Monolito modular vs microservicios: marco de decisión#

| Situación | Punto de partida razonable | | -------------------------------------------------- | -------------------------------------- | | Un equipo de producto pequeño o mediano | Monolito modular | | Los límites del dominio todavía evolucionan | Monolito modular | | Refactors frecuentes entre dominios | Monolito modular | | Workflows transaccionales locales fuertes | Monolito modular | | Poca experiencia con sistemas distribuidos | Monolito modular | | Varios equipos autónomos | Considerar microservicios | | El despliegue independiente es crítico | Considerar microservicios | | Componentes con perfiles de escalado muy distintos | Considerar microservicios | | Se necesita fuerte aislamiento en runtime | Considerar microservicios | | Los límites de servicio ya son estables | Los microservicios son menos riesgosos |

No es un algoritmo.

Es una forma de forzar la pregunta correcta:

¿qué problema concreto estamos intentando resolver mediante distribución?

El principio arquitectónico más importante#

Modularidad y distribución son decisiones distintas.

Podemos tener:

un monolito mal diseñado
un monolito bien diseñado
microservicios mal diseñados
microservicios bien diseñados

Distribuir no crea buenas fronteras.

Hace más costosa y explícita la comunicación entre ellas.

Un monolito modular permite establecer primero la disciplina:

contratos explícitos
ownership claro
alta cohesión
bajo acoplamiento
límites de dominio
dependencias controladas

sin asumir inmediatamente el coste operativo de un sistema distribuido.

Por eso también es útil cuando los microservicios podrían convertirse en el destino final.

Quizá especialmente en ese caso.

Preguntas frecuentes#

¿Qué es un monolito modular?#

Es una aplicación desplegable como una sola unidad, pero dividida internamente en módulos explícitos con responsabilidades y dependencias controladas.

¿Es lo mismo que microservicios?#

No.

Un monolito modular crea fronteras lógicas dentro de una misma aplicación. Los microservicios introducen fronteras de runtime desplegables de forma independiente que se comunican mediante red o infraestructura de mensajería.

¿Es mejor que los microservicios?#

Ninguna arquitectura es universalmente mejor.

Un monolito modular suele tener un coste operativo menor cuando no necesitas despliegue o escalado independientes.

Los microservicios se vuelven más atractivos cuando la independencia técnica u organizativa resuelve un problema concreto.

¿Puede escalar un monolito modular?#

Sí.

La aplicación completa puede escalar horizontal o verticalmente.

Lo que normalmente no permite es escalar un módulo interno de manera independiente. Si una capacidad desarrolla un perfil de escalado radicalmente distinto, eso puede convertirse en una razón para extraerla.

¿Cada módulo necesita su propia base de datos?#

No.

Varios módulos pueden compartir una instancia física de PostgreSQL y mantener ownership explícito sobre determinados schemas o tablas.

Lo importante es impedir que otros módulos ignoren esa frontera accediendo directamente a la persistencia ajena.

¿Cómo deberían comunicarse los módulos?#

Mediante llamadas síncronas a APIs explícitas cuando se requiere una respuesta inmediata y mediante eventos cuando otros módulos deben reaccionar sin convertirse en dependencias directas del productor.

Los eventos pueden ser síncronos o asíncronos, durables o únicamente in-memory. Son decisiones distintas.

¿Puede convertirse más adelante en microservicios?#

Sí.

Un módulo con límites claros, contratos explícitos y ownership de datos puede ser un buen candidato para extracción.

Eso no elimina la necesidad de resolver fallos de red, autenticación, observabilidad, retries, migración de datos y consistencia entre servicios.

¿Debería una startup empezar con microservicios?#

Solo cuando sus restricciones técnicas u organizativas realmente lo justifiquen.

Para muchos equipos pequeños, un monolito modular permite que el dominio y sus fronteras evolucionen sin introducir infraestructura distribuida desde el inicio.

Conclusión#

Los microservicios no son lo contrario de una mala arquitectura.

La buena modularidad sí.

Si un monolito se ha convertido en un big ball of mud, mover el mismo grafo de dependencias a través de una red no va a corregirlo.

Empieza por las fronteras.

Define ownership.

Protege los internals de cada módulo.

Expón contratos explícitos.

Usa eventos cuando eliminen dependencias innecesarias.

Prueba las reglas arquitectónicas.

Después observa el sistema.

Si un módulo termina necesitando despliegue, escalado, ownership, infraestructura o aislamiento de fallos independientes, extráelo.

En ese momento la frontera de servicio ya no existe por moda ni por anticipación.

Existe porque el propio sistema ha demostrado que la independencia física aporta valor.

Y esa es una razón mucho más sólida para adoptar microservicios.