Saltar al contenido

Patrón Strangler Fig: migrar un monolito paso a paso, con diagrama

Cómo reemplazar un monolito con el patrón strangler fig: fachada de enrutamiento, cuatro fases, qué extraer primero, datos sin doble escritura y prueba de paridad.

· Fundador y desarrollador

Publicado el

Los clientes llaman a un API gateway que hace de fachada. Envía /catalog/* (100 por ciento) a un servicio de catálogo con su base, 25 por ciento de GET /orders/* a un servicio de pedidos y el resto al monolito, cuya base alimenta la de pedidos por una ruta CDC discontinua.

Tu monolito tiene diez años, cada versión necesita un largo ciclo de regresión y alguien propone una reescritura: construir el sistema nuevo en paralelo, alcanzar la paridad de funciones y cambiar un fin de semana. Mientras tanto, el sistema antiguo sigue cambiando y la fecha del cambio se aplaza. Martin Fowler escribe que él y sus colegas han visto este plan «irse al traste la mayoría de las veces».

El patrón strangler fig reemplaza el sistema por piezas. Una fachada delante del monolito enruta cada petición al código antiguo o a un servicio nuevo, y las capacidades se mueven una a una hasta que nada se enruta al monolito. El enrutamiento es la parte fácil. La propiedad de los datos, probar la paridad y terminar son las difíciles.

Este artículo cubre todo eso, con un diagrama de una migración en curso.

De dónde viene el nombre

En 2001, Fowler vio higueras estranguladoras en la selva de Queensland: enredaderas que germinan sobre un árbol huésped, echan raíces en el suelo y pueden sobrevivir al árbol, dejando la higuera con su forma. En junio de 2004 las usó como metáfora para reemplazar un sistema heredado poco a poco. En abril de 2019 cambió el nombre del artículo de «Strangler Application» a «Strangler Fig Application», para remitir a la planta, y lo reescribió en agosto de 2024. La versión actual subraya lo que los equipos rechazan: la arquitectura de transición, código que escribes solo para que lo viejo y lo nuevo convivan.

La fachada hace el enrutamiento

La fachada es un reverse proxy o un API gateway delante del monolito: NGINX, Envoy, un API gateway de la nube o un balanceador de carga con reglas de ruta. Empieza enviándolo todo al monolito. Después, cada paso de la migración es un cambio de enrutamiento. Enruta por:

  • Ruta: /catalog/* va al servicio de catálogo.
  • Método: GET /orders/* antes que POST /orders, para que las lecturas se muevan primero.
  • Porcentaje: un hash de un ID de sesión o de usuario mantiene a cada usuario en un solo lado.
  • Cabecera o tenant: los usuarios internos o un cliente piloto van primero.
# upstream blocks catalog, orders and monolith defined elsewhere
split_clients "${cookie_session}" $orders_read {
    25%      orders;       # canary share for reads
    *        monolith;
}
map $request_method $orders_target {
    GET      $orders_read;
    default  monolith;     # order writes stay in the monolith
}
server {
    location /catalog/ { proxy_pass http://catalog; }
    location /orders/  { proxy_pass http://$orders_target; }
    location /         { proxy_pass http://monolith; }
}

El rollback es una sola edición: apunta la línea del 25 % a monolith (nginx rechaza una entrada del 0 %). Aplica el hash a una clave que lleve toda petición: las peticiones sin la cookie caen todas en un mismo bucket.

Mantén la fachada delgada: enrutamiento, TLS, autenticación y logging, sin reglas de negocio. Está en cada petición, así que ejecuta varias instancias y mide la latencia que añade. El diagrama de arquitectura de un API gateway explica qué pertenece a un gateway.

Las cuatro fases de una migración

Fase La fachada enruta Datos Terminada cuando
0. Primero la fachada Todo al monolito Sin cambios Fachada en producción, latencia añadida medida
1. Primera capacidad Una ruta, como /catalog/*, a un servicio nuevo El servicio lee las tablas del monolito o una copia CDC, luego las suyas Ruta al 100 % durante un ciclo de negocio completo
2. Casi todo el tráfico en servicios nuevos La mayoría de las rutas; el monolito conserva una lista conocida Cada servicio es dueño de sus tablas Lo que queda cabe en una página
3. Monolito retirado Nada al monolito Base heredada archivada y luego borrada Monolito apagado

La página del patrón Strangler Fig de Microsoft añade un paso final: quitar la fachada, o conservarla como adaptador para clientes heredados.

Elegir qué extraer primero

Los buenos primeros candidatos tienen tres rasgos:

  • Una costura clara, en palabras de Michael Feathers un lugar donde puedes alterar el comportamiento sin editar en ese lugar: un prefijo de URL, una cola, el límite de un módulo.
  • Una tasa de cambio alta, para que el movimiento compense pronto. El control de versiones la muestra.
  • Bajo acoplamiento: pocos joins con otras tablas, pocas llamadas de vuelta al monolito.
git log --since="12 months ago" --format= --name-only -- src/ \
  | sort | uniq -c | sort -rn | head -20

Un ranking para una tienda en línea:

Candidato Costura Tasa de cambio Acoplamiento Veredicto
Navegación del catálogo /catalog/* Alta Bajo Primero
Historial de pedidos GET /orders/* Media Medio Segundo, lecturas primero
Checkout Llama a precios, stock y pagos Alta Alto Después de sus dependencias
Informes nocturnos SQL sobre todo el esquema Baja Muy alto Último, o un feed a un almacén de datos

Extrae por capacidad de negocio, no por capa técnica. Un «servicio de acceso a datos» compartido no mueve nada y acopla todo.

Mover los datos

El enrutamiento mueve peticiones, no datos, y en los datos es donde se estancan las migraciones. Decide quién es dueño de cada tabla en cada paso.

Estrategia Cómo funciona Úsala para Riesgo
Base de datos compartida El servicio nuevo usa las tablas del monolito Las primeras semanas Los cambios de esquema del monolito lo rompen
Change data capture Un conector emite los cambios de filas desde el log del monolito Mover primero las lecturas Retraso, esquemas heredados, flujo en un solo sentido
Base de datos por servicio El servicio es dueño de sus tablas; los demás llaman a su API El estado final Los joins entre servicios se vuelven llamadas a API
Doble escritura desde el código La aplicación escribe en ambas bases Nada Un fallo entre las dos escrituras las deja desincronizadas

La guía de Microsoft da el orden habitual: el servicio nuevo empieza sobre las tablas del monolito, luego obtiene su propia base, cargada una vez y mantenida al día por CDC, y pasa a ser el sistema de registro tras la validación. Mientras existan las tablas heredadas, el rollback es un cambio de ruta; después, una restauración.

Evita la doble escritura: un fallo entre dos escrituras separadas deja las bases en desacuerdo, y las actualizaciones concurrentes pueden llegar en distinto orden. Haz que una sola transacción sea el origen de cada cambio, mediante CDC o un transactional outbox. El diagrama de change data capture cubre Debezium y los replication slots.

La capa anticorrupción

El modelo del monolito se filtra: códigos de estado de una letra, campos de facturación y envío en una sola tabla, reglas en procedimientos almacenados. Un servicio que consume esas filas hereda el modelo que estás dejando. Una capa anticorrupción, un patrón del Domain-Driven Design de Eric Evans (2003), traduce en la frontera:

  • De heredado a nuevo: los eventos CDC con filas heredadas se convierten en pedidos del modelo nuevo.
  • De nuevo a heredado: el servicio de pedidos lee datos de clientes del monolito a través de un adaptador.
  • De monolito a nuevo: una vez movido el catálogo, el código del monolito llama a la API del catálogo en lugar de a sus antiguas tablas.

Mantén las reglas de negocio fuera de la capa. En un servicio hexagonal vive en los adaptadores, así que el núcleo nunca ve tipos heredados.

Probar la paridad antes de mover una ruta

Cada ruta pasa por los mismos pasos:

  1. Shadow. La fachada copia las peticiones al servicio nuevo y descarta sus respuestas, como hacen el módulo mirror de NGINX y el request mirroring de Envoy. Replica solo lecturas: un POST /orders replicado crea un segundo pedido.
  2. Comparar. Compara ambas respuestas tras normalizar marcas de tiempo, IDs y orden. Diffy de Twitter, publicado como código abierto en 2015, ejecuta dos copias del código antiguo e ignora como ruido lo que difiere entre ellas. Dentro del monolito, la biblioteca Scientist de GitHub (Ruby, con ports a otros lenguajes) ejecuta en paralelo la ruta antigua y la nueva y devuelve el resultado de la antigua.
  3. Canary. Mueve el 1 %, 5 %, 25 %, 50 % y luego 100 % del tráfico real, compara la tasa de errores y la latencia p99 con las del monolito para la misma ruta y mantén cada paso durante un pico diario. El diagrama de estrategias de despliegue cubre la mecánica.
  4. Mantener. Al 100 %, conserva la ruta antigua desplegable y sus datos al día durante eventos como un cierre de fin de mes.
  5. Borrar el código antiguo, sus tablas y la ruta.

Devolver una ruta solo funciona mientras el monolito tenga datos al día. Cuando el servicio nuevo sea dueño de las escrituras, devuélvelas mediante CDC inverso o eventos, o un rollback las perderá.

Cómo fracasan las migraciones strangler fig

  • No terminar nunca. Se mueve el 80 % fácil; los informes, los trabajos por lotes y las pantallas de administración se quedan, y operas dos sistemas. Cuenta lo que queda en cada sprint y fija una fecha de apagado.
  • Un monolito distribuido. Servicios que comparten tablas, se despliegan juntos o se llaman en largas cadenas síncronas. La prueba es un escritor por tabla.
  • La fachada como cuello de botella: lógica de negocio en el gateway, o una sola instancia que lleva todo el tráfico.
  • Consumidores ocultos. Informes y otros equipos leen directamente la base del monolito, sin que la fachada lo vea. Revisa los logs de conexión antes de mover una tabla.
  • La paridad de funciones como objetivo. Fowler señala que gran parte del comportamiento heredado «en realidad no se quiere». Patterns of Legacy Displacement cubre esta y otras trampas.

El diagrama de la migración strangler fig

Los clientes llaman a un API gateway que hace de fachada. Envía /catalog/* (100 por ciento) a un servicio de catálogo con su base, 25 por ciento de GET /orders/* a un servicio de pedidos y el resto al monolito, cuya base alimenta la de pedidos por una ruta CDC discontinua.

17 componentes · 10 conexiones

Abrir en Archboard
Contenido del diagrama
  • Customers
  • Facade (API gateway)
  • Catalog service
  • Orders service
  • Legacy monolith
  • Catalog DB
  • Orders DB
  • Monolith DB
  • Debezium + Kafka
  • Anti-corruption layer
  • HTTPS
  • /catalog/* · 100%
  • GET /orders/* · 25%
  • everything else + 75% of GET /orders
  • reads, writes
  • reads
  • WAL changes
  • order row events
  • upserts in the new model
Una migración de monolito en curso. La fachada envía todo el tráfico del catálogo a un servicio nuevo dueño de sus datos, un cuarto de las lecturas de pedidos a un servicio alimentado por change data capture y el resto al monolito. Cada paso es reversible.

Cómo leerlo:

  • De izquierda a derecha: los clientes llaman a la fachada, que reparte el tráfico por ruta; las bases de datos están a la derecha.
  • Verde, movido: todo /catalog/* va al servicio de catálogo, que es dueño de sus datos.
  • Ámbar, canary: el 25 % de GET /orders/* va al servicio de pedidos. Las escrituras y las demás lecturas siguen yendo al monolito.
  • Gris, heredado: el monolito atiende el resto y sigue siendo dueño de los datos de pedidos.
  • Morado, discontinuo: Debezium emite los cambios de pedidos desde el WAL del monolito a través de Kafka, y la capa anticorrupción los mapea al modelo nuevo.
  • La tabla de rutas lista destino, porcentaje y fase por ruta, así que el rollback es una sola edición.

Dibujar con claridad las migraciones strangler fig

  • Un diagrama por fase, o un título con fecha; conserva los antiguos para mostrar la dirección.
  • Etiqueta las rutas con ruta y porcentaje, como «GET /orders/* · 25 %».
  • Muestra la propiedad de los datos: el sistema de registro y la dirección de cada sincronización.
  • Colorea por estado (movido, canary, heredado), no por equipo.
  • Deja fuera los detalles internos. Esto es un mapa de migración, no un diagrama de componentes.
  • Dibuja los consumidores ocultos, como un informe nocturno que lee la base del monolito.

El diagrama de arquitectura de microservicios muestra cómo mantener legible el estado final.

Constrúyelo en Archboard

  1. Inicia sesión con Google o GitHub y crea un diagrama.
  2. Añade una Persona del pack C4 Model para los clientes y una Puerta de enlace de API del pack Nube para la fachada.
  3. Añade tres nodos Contenedor del pack C4 Model: Catalog service, Orders service y Legacy monolith. Coloréalos de verde, ámbar y gris.
  4. Añade un nodo Postgres del pack Datos a la derecha de cada contenedor.
  5. Entre la base del monolito y la base de pedidos, apila un nodo Kafka del pack Datos (Debezium + Kafka) y un Componente del pack C4 Model (Anti-corruption layer), conectados hacia arriba con aristas discontinuas.
  6. Dibuja las rutas desde la fachada, etiquetadas con ruta y porcentaje, y añade encima de cada contenedor una etiqueta de estado Texto del pack Básico.
  7. Añade un bloque Markdown del pack Básico con la tabla de rutas.

Si tu mapa de rutas vive en un flowchart de Mermaid, el conversor de Mermaid lo convierte en un primer borrador editable.

El ejemplo usa 11 nodos de diagrama, dentro del plan Free.

Preguntas frecuentes

¿Qué es el patrón strangler fig? Una forma de reemplazar un sistema heredado poco a poco: una fachada enruta cada petición al código antiguo o a un servicio nuevo, las capacidades se mueven una a una y el sistema antiguo se apaga cuando nada se enruta a él.

¿Por qué se llama patrón strangler fig? Martin Fowler lo nombró en 2004 por las higueras estranguladoras que vio en Queensland, que crecen alrededor de un árbol huésped y pueden reemplazarlo. Añadió Fig al nombre en 2019 para destacar la planta.

¿Cómo se maneja la base de datos en una migración strangler fig? Por pasos. El servicio nuevo primero lee las tablas del monolito, luego obtiene su propia base alimentada por change data capture y asume las escrituras tras la validación. Evita la doble escritura desde el código de la aplicación.

¿Cuál es la diferencia entre strangler fig y branch by abstraction? El strangler fig enruta peticiones en el borde del sistema. Branch by abstraction actúa dentro del código: una interfaz permite que convivan la implementación antigua y la nueva hasta que todos los llamadores hayan cambiado. Muchas migraciones usan ambos.

¿Cuándo no se debe usar el patrón strangler fig? Cuando las peticiones al sistema antiguo no se pueden interceptar, cuando el sistema es lo bastante pequeño para reemplazarlo de una vez o cuando hay que apagarlo rápido.

Empieza gratis — un puesto, sin tarjeta de crédito.

Artículos relacionados

← Volver al blog

Abre un lienzo en un minuto

Inicia sesión con Google o GitHub. Gratis para un asiento. Mejora el plan cuando se una el equipo.