Saltar a contenido

Dynamic Router

1. Nombre del Patrón

  • Nombre oficial: Dynamic Router
  • Categoría: Message Routing (Enrutamiento de Mensajes)
  • Traducción contextual: Enrutador Dinámico

2. Resumen Ejecutivo

Dynamic Router es el patrón que extiende el concepto de Content-Based Router permitiendo que las reglas de routing se modifiquen en tiempo de ejecución, sin necesidad de redesplegar el componente de routing. Mientras que un Content-Based Router opera con reglas estáticas definidas en diseño o configuración, un Dynamic Router obtiene y actualiza sus reglas desde un almacén externo o mediante mensajes de control, adaptándose a cambios en la topología de consumidores, requisitos de negocio o condiciones operacionales sin interrupción del servicio.

El problema que resuelve es la rigidez del routing estático en entornos donde los destinos, las condiciones o los consumidores cambian con frecuencia: tenants que se onboardean y offboardean en una plataforma SaaS, feature flags que activan o desactivan rutas de procesamiento, A/B testing que distribuye tráfico según configuración variable, o regulaciones que requieren cambios de routing inmediatos. En todos estos escenarios, un router cuyas reglas requieren redespliegue para cambiar es un obstáculo operacional.

La implementación moderna del Dynamic Router se manifiesta en configuration stores como AWS AppConfig, Azure App Configuration, Consul KV, etcd y feature flag platforms como LaunchDarkly o Split.io. El patrón también aparece en plataformas de API management donde las rutas de tráfico se configuran dinámicamente, y en control buses que envían comandos de reconfiguración a componentes de routing en tiempo real.


3. Definición Detallada

Propósito

Dynamic Router recibe mensajes de un canal de entrada y los dirige a canales de salida basándose en reglas que no están fijas en el código o la configuración estática del componente, sino que se obtienen dinámicamente de un almacén de reglas externo. Cuando las reglas cambian en el almacén, el router refleja los cambios automáticamente, sin redespliegue ni reinicio.

Lógica Arquitectónica

Un Content-Based Router con reglas estáticas funciona perfectamente cuando los destinos y las condiciones de routing son estables a lo largo del tiempo. Pero en muchas arquitecturas modernas, la topología de routing es inherentemente dinámica:

  • SaaS multi-tenant: cada tenant puede tener configuraciones de procesamiento diferentes. Cuando un nuevo tenant se onboardea, se debe añadir una regla de routing para dirigir sus mensajes al pipeline correcto. Cuando un tenant se offboardea, la regla debe eliminarse.
  • Feature flags: una nueva funcionalidad se habilita gradualmente. Los mensajes de usuarios en el grupo de prueba deben enrutarse al nuevo procesador; los del grupo de control, al procesador existente.
  • A/B routing: el 10% del tráfico se dirige a una versión nueva del servicio para medir su comportamiento. El porcentaje se ajusta en tiempo real según los resultados.
  • Regulación: una nueva regulación requiere que ciertos tipos de transacciones se procesen en una jurisdicción específica a partir de una fecha determinada.

En todos estos casos, las reglas de routing deben poder cambiar sin interrupción del servicio.

Mecanismos de Actualización de Reglas

El Dynamic Router puede obtener sus reglas de múltiples formas:

  • Polling: el router consulta periódicamente un almacén externo (base de datos, config store, API) para obtener las reglas vigentes. Simple pero introduce latencia entre el cambio de la regla y su aplicación.
  • Push via Control Bus: un canal de control envía mensajes de actualización al router cuando las reglas cambian. Más reactivo que polling pero requiere infraestructura de control.
  • Subscription a config store: el router se suscribe a cambios en un almacén de configuración (Consul watch, AWS AppConfig polling, etcd watch). Ofrece notificación en near-real-time.
  • Event-driven: los cambios de reglas se modelan como eventos (TenantOnboarded, FeatureFlagChanged, RoutingRuleUpdated) que el router consume.

Almacén de Reglas

Las reglas del Dynamic Router se almacenan externamente:

  • Base de datos: tabla de routing con campos condition, destination, priority, active, effective_from, effective_until.
  • Configuration store: Consul KV, etcd, AWS AppConfig, Azure App Configuration.
  • Feature flag platform: LaunchDarkly, Split.io, Unleash (para routing basado en features o segmentos de usuarios).
  • Config file con hot reload: un archivo de configuración (YAML/JSON) en un volumen compartido o config map que el router recarga periódicamente.

Diferencia con Content-Based Router

Aspecto Content-Based Router Dynamic Router
Reglas Fijas en código/config Modificables en runtime
Cambio de reglas Requiere redespliegue Sin redespliegue
Complejidad Baja Media
Fuente de reglas Código o config estática Almacén externo
Latencia de cambio Minutos-horas (deploy) Segundos-minutos (config)

Contexto en el que Emerge

Dynamic Router emerge en plataformas multi-tenant, sistemas con feature flags, arquitecturas que requieren A/B testing o canary deployment a nivel de mensajería, y cualquier escenario donde la topología de routing es un aspecto configurable del negocio, no un aspecto fijo de la arquitectura.


4. Problema que Resuelve

El Problema Antes del Patrón

Consideremos una plataforma SaaS B2B que procesa documentos electrónicos (facturas, órdenes de compra, avisos de envío) para 200 tenants corporativos. Cada tenant tiene requisitos de procesamiento diferentes:

  • Tenant A: facturas en formato UBL procesadas vía API REST al ERP SAP.
  • Tenant B: facturas en formato EDIFACT procesadas vía SFTP al sistema legacy.
  • Tenant C: facturas en formato Facturae procesadas vía web service SOAP al sistema de la Agencia Tributaria.

Con un Content-Based Router estático, las 200 reglas de routing (una por tenant) están en la configuración del router. Cuando se onboardea el tenant 201, se debe:

  1. Actualizar la configuración del router.
  2. Testar la nueva configuración.
  3. Redesplegar el router.
  4. Validar que las 200 reglas existentes siguen funcionando.

Este proceso puede tomar horas o días, e introduce riesgo de regresión para los 200 tenants existentes.

Síntomas del Problema

  • Ciclos de despliegue del router que retrasan el onboarding de nuevos clientes.
  • Riesgo de regresión en routing existente por cambios para nuevos tenants.
  • Incapacidad de responder a cambios de negocio urgentes (una nueva regulación, un tenant premium que necesita una ruta especial) sin un ciclo completo de deploy.
  • Configuración de routing creciente y monolítica que se vuelve difícil de testar exhaustivamente.
  • Equipos de operaciones que dependen del equipo de desarrollo para cambios de routing rutinarios.

Impacto Operativo y Arquitectónico

Sin routing dinámico:

  • El onboarding de un nuevo tenant se convierte en un proyecto de desarrollo con ciclo de release, en lugar de una operación de configuración.
  • Los cambios de routing urgentes requieren despliegues de emergencia, con riesgo asociado.
  • La plataforma no puede ofrecer self-service de configuración a los tenants.
  • La velocidad de crecimiento del negocio está limitada por la velocidad de los ciclos de despliegue.

Riesgos Si No Se Implementa Correctamente

  • Inconsistencia de reglas: si múltiples instancias del router no leen las reglas al mismo tiempo, pueden aplicar reglas diferentes durante la ventana de transición.
  • Reglas inválidas: una regla mal formada en el almacén externo puede causar errores de routing en runtime (en lugar de detectarse en tiempo de compilación o despliegue).
  • Latencia de propagación: el tiempo entre el cambio de regla y su aplicación efectiva puede causar mensajes enrutados con reglas obsoletas.
  • Falta de rollback: si una nueva regla produce routing incorrecto, ¿cuánto se tarda en revertir? Sin un mecanismo de rollback explícito, el tiempo de recuperación puede ser largo.

Ejemplos Reales

  • SaaS B2B: plataforma de intercambio electrónico de documentos (EDI) que enruta facturas de cada tenant a su sistema de destino específico. Nuevos tenants se onboardean semanalmente.
  • Plataforma de pagos: routing de transacciones a diferentes procesadores de pago según el tipo de tarjeta, el país y el monto. Los procesadores y sus condiciones cambian frecuentemente por acuerdos comerciales.
  • CDN: routing de requests a diferentes origin servers según reglas de geolocalización, tipo de contenido y estado de los servidores. Las reglas cambian dinámicamente según la carga.

5. Contexto de Aplicación

Cuándo Usarlo

  • Cuando las reglas de routing cambian con frecuencia (semanal o más frecuente) y el costo de redespliegue es significativo.
  • Cuando los cambios de routing deben poder realizarse por personal de operaciones o negocio, no solo por desarrolladores.
  • Cuando la plataforma es multi-tenant y cada tenant puede requerir configuración de routing diferente.
  • Cuando se implementa feature flagging, A/B testing o canary routing a nivel de mensajería.

Cuándo No Usarlo

  • Si las reglas de routing son estables (cambian menos de una vez al mes), un Content-Based Router con configuración estática es más simple y suficiente.
  • Si el número de destinos es pequeño y fijo (2-5), la complejidad del routing dinámico no se justifica.
  • Si la correctitud del routing es crítica para la seguridad y los cambios deben pasar por un proceso formal de revisión y despliegue, el routing estático con pipeline de CI/CD ofrece mejor control.

Precondiciones

  • Existe un almacén de reglas externo disponible y confiable.
  • Las reglas se pueden expresar de forma declarativa (no requieren lógica imperativa compleja).
  • Existe un mecanismo para notificar o detectar cambios en las reglas.
  • Los consumidores downstream existen y están preparados antes de que una regla los referencie.

Restricciones

  • La latencia de propagación de reglas debe ser aceptable para el caso de uso.
  • Todas las instancias del router deben converger a las mismas reglas en un tiempo acotado.
  • Las reglas inválidas deben detectarse y rechazarse antes de aplicarse (validación).

Dependencias

  • Almacén de reglas externo con alta disponibilidad.
  • Mecanismo de actualización (polling, watch, event).
  • Validación de reglas antes de aplicación.
  • Canales de salida pre-creados o auto-creados cuando una regla los referencia.

Supuestos Arquitectónicos

  • El almacén de reglas es más disponible que el propio router (si el almacén cae, el router debe seguir operando con las últimas reglas conocidas).
  • Los cambios de reglas son poco frecuentes comparados con el volumen de mensajes (las reglas se cachean, no se consultan por cada mensaje).
  • Las reglas son válidas y han sido validadas antes de persistirse en el almacén.

Tipo de Sistemas Donde Aparece con Más Frecuencia

  • Plataformas SaaS multi-tenant.
  • Sistemas de integración B2B (EDI, API management).
  • Plataformas de pagos y finanzas con routing a procesadores variables.
  • Sistemas con feature flags y A/B testing.
  • CDNs y load balancers con routing configurable.

6. Fuerzas Arquitectónicas

Flexibilidad vs. Seguridad

La capacidad de cambiar reglas en runtime ofrece máxima flexibilidad operacional. Pero también introduce riesgo: una regla incorrecta aplicada en runtime afecta inmediatamente al tráfico en producción. Con routing estático, los cambios pasan por CI/CD con tests; con routing dinámico, deben existir mecanismos equivalentes de validación.

Consistencia vs. Disponibilidad

Cuando las reglas cambian, ¿todas las instancias del router ven las nuevas reglas simultáneamente? En la práctica, no: hay una ventana de propagación donde algunas instancias tienen las reglas nuevas y otras las antiguas. Durante esta ventana, mensajes similares pueden enrutarse a destinos diferentes según la instancia que los procese.

Simplicidad vs. Potencia

Un Content-Based Router estático es simple: las reglas están ahí, visibles, testeables. Un Dynamic Router introduce complejidad: almacén externo, mecanismo de actualización, cache, invalidación, validación, rollback. Esta complejidad se justifica solo cuando la frecuencia de cambio lo requiere.

Latencia de Configuración vs. Latencia de Mensaje

Cargar reglas desde un almacén externo para cada mensaje es exacto pero lento. Cachear las reglas es rápido pero introduce latencia de configuración (el cambio tarda en reflejarse). El compromiso típico es cache con TTL corto o invalidación por notificación.

Centralización de Reglas vs. Autonomía de Tenants

¿Quién gestiona las reglas? Si las gestiona un equipo centralizado, hay control pero poca agilidad. Si cada tenant gestiona su propia regla (self-service), hay agilidad pero riesgo de conflictos, reglas inválidas y falta de visibilidad global.

Costo Operacional vs. Velocidad de Negocio

Mantener un almacén de reglas externo con validación, versionado, rollback y monitoreo tiene costo. Pero este costo se justifica cuando el negocio necesita cambios de routing frecuentes y rápidos que un ciclo de despliegue no puede ofrecer.


7. Estructura Conceptual del Patrón

Actores o Componentes Involucrados

  1. Productor (Sender): envía mensajes al canal de entrada sin conocimiento de routing.
  2. Canal de Entrada (Input Channel): canal donde llegan mensajes para routing.
  3. Dynamic Router: componente que consume mensajes y los enruta según reglas dinámicas.
  4. Almacén de Reglas (Rule Store): repositorio externo donde se persisten y gestionan las reglas.
  5. Cache de Reglas (Rule Cache): copia en memoria de las reglas activas, cacheada para performance.
  6. Canal de Control (Control Bus): canal opcional por donde se envían comandos de actualización de reglas.
  7. Canales de Salida (Output Channels): canales de destino referenciados por las reglas.
  8. Consumidores (Receivers): procesan mensajes en los canales de salida.
  9. Administrador de Reglas (Rules Admin): persona o sistema que gestiona las reglas en el almacén.

Flujo Lógico

flowchart TD
    subgraph Configuración ["Flujo de Configuración (asíncrono)"]
        X([Admin]) --> X1[Crear/modificar/eliminar regla]
        X1 --> X2[(Almacén de Reglas)]
        X2 --> X3[Router detecta cambio\npoll, watch o evento]
        X3 --> X4[Validar nueva regla]
        X4 --> X5[(Cache de Reglas\nactualizado)]
    end

    subgraph Mensajes ["Flujo de Mensajes (síncrono)"]
        A([Productor]) --> B[(Canal de Entrada)]
        B --> C[Router consume mensaje]
        C --> D[(Cache de Reglas\nen memoria)]
        D --> E[Evaluar reglas contra\ncontenido del mensaje]
        E --> F[Identificar canal de salida]
        F --> G[(Canal de Salida)]
        G --> H[Hacer ack del\ncanal de entrada]
        H --> I([Fin])
    end

Responsabilidades

Componente Responsabilidad
Productor Enviar mensajes con información suficiente para routing
Dynamic Router Consumir, evaluar reglas dinámicas, enrutar, cachear reglas
Almacén de Reglas Persistir reglas, ofrecer versionado y rollback
Cache de Reglas Ofrecer acceso sub-millisecond a reglas vigentes
Canal de Control Transmitir comandos de actualización de reglas
Admin de Reglas Gestionar el ciclo de vida de las reglas

Interacciones

  • Admin → Almacén: CRUD de reglas con validación.
  • Almacén → Router: notificación de cambios (push) o consulta periódica (pull).
  • Router → Cache: actualización atómica de reglas.
  • Mensaje → Router → Cache → Canal de Salida: flujo de routing en hot path.

Contratos Implícitos

  • Las reglas en el almacén son válidas (han pasado validación).
  • El canal de salida referenciado por una regla existe y tiene consumidores activos.
  • El cache se invalida correctamente cuando las reglas cambian.
  • Las reglas convergen en todas las instancias del router en un tiempo acotado.

Decisiones de Diseño Clave

  1. Polling vs. push: ¿el router consulta periódicamente o recibe notificaciones?
  2. Cache TTL vs. invalidación explícita: ¿las reglas expiran por tiempo o se invalidan por evento?
  3. Validación de reglas: ¿dónde se valida (en el almacén al persistir, en el router al cargar)?
  4. Fallback: si el almacén no está disponible, ¿el router usa las últimas reglas conocidas o detiene el routing?
  5. Atomicidad de actualización: ¿se actualizan todas las reglas atómicamente o pueden coexistir reglas de diferentes versiones?

8. Ejemplo Arquitectónico Detallado

Dominio: SaaS B2B — Routing Dinámico de Documentos por Tenant

Contexto del Negocio

Una plataforma SaaS de intercambio electrónico de documentos conecta a 200 empresas (tenants) que intercambian facturas, órdenes de compra y avisos de envío. Cada tenant tiene requisitos específicos de procesamiento:

  • Formato de documento: UBL, EDIFACT, Facturae, cXML, ANSI X12.
  • Protocolo de entrega: API REST, SFTP, AS2, SOAP, email.
  • Transformaciones: mapeos de campos específicos por tenant.
  • Validaciones: reglas de negocio específicas por industria y país.

La plataforma onboardea 5-10 nuevos tenants por semana y los configura sin ciclos de despliegue.

Necesidad de Integración

Los documentos entrantes llegan a un canal unificado y deben dirigirse al pipeline de procesamiento correcto para cada tenant. Dado que los tenants cambian semanalmente (onboardings, offboardings, cambios de configuración), un router estático es inviable.

Sistemas Involucrados

  1. Document Intake Service: recibe documentos de todos los canales (API, SFTP, AS2, email) y los normaliza.
  2. Dynamic Router: dirige cada documento al pipeline de procesamiento correcto basándose en reglas almacenadas en el Rule Store.
  3. Rule Store (PostgreSQL + Cache): base de datos con la configuración de routing por tenant.
  4. Rule Admin UI: interfaz web donde el equipo de operaciones gestiona las reglas.
  5. Pipeline Processors: un conjunto de pipelines de procesamiento configurados para cada combinación de formato/protocolo/transformación.
  6. Unroutable Handler: maneja documentos que no coinciden con ninguna regla.

Restricciones Técnicas

  • Latencia de routing < 100ms por documento.
  • Las reglas nuevas deben estar activas en < 30 segundos tras su creación.
  • Cero pérdida de documentos durante cambios de reglas.
  • Auditoría completa: qué regla se aplicó a cada documento.
  • Alta disponibilidad: el router opera 24/7, los cambios de reglas no requieren reinicio.

Diseño del Dynamic Router

Almacén de Reglas (PostgreSQL)

CREATE TABLE routing_rules (
    id UUID PRIMARY KEY,
    tenant_id VARCHAR(50) NOT NULL,
    condition_field VARCHAR(100) NOT NULL,
    condition_operator VARCHAR(20) NOT NULL,
    condition_value VARCHAR(200) NOT NULL,
    destination_channel VARCHAR(200) NOT NULL,
    priority INT NOT NULL DEFAULT 100,
    active BOOLEAN NOT NULL DEFAULT true,
    effective_from TIMESTAMP NOT NULL DEFAULT NOW(),
    effective_until TIMESTAMP,
    created_by VARCHAR(100) NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    version INT NOT NULL DEFAULT 1
);

Ejemplo de Reglas

tenant_id condition destination priority
TENANT-ACME header.tenant_id == 'TENANT-ACME' pipeline.ubl-rest-sap 100
TENANT-GLOBEX header.tenant_id == 'TENANT-GLOBEX' pipeline.edifact-sftp-legacy 100
TENANT-INITECH header.tenant_id == 'TENANT-INITECH' pipeline.facturae-soap-aeat 100
DEFAULT true pipeline.unroutable 999

Mecanismo de Actualización

  1. El operador crea una regla para el nuevo tenant en la Rule Admin UI.
  2. La UI persiste la regla en PostgreSQL y publica un evento RoutingRuleChanged en un canal de control.
  3. Todas las instancias del Dynamic Router reciben el evento y recargan su cache de reglas desde PostgreSQL.
  4. El cache se actualiza atómicamente (swap de referencia al nuevo conjunto de reglas).
  5. El próximo mensaje se evalúa contra el nuevo conjunto de reglas.

Decisiones Arquitectónicas

  1. PostgreSQL como source of truth + cache in-memory: las reglas se almacenan en PostgreSQL para durabilidad, versionado y consultas. El router mantiene un cache en memoria (HashMap) para evaluación sub-millisecond. El evento de control Bus invalida el cache.

  2. Evento de control para propagación: en lugar de polling (latencia de hasta TTL), un evento RoutingRuleChanged notifica a todas las instancias del router inmediatamente. Tiempo de propagación típico: < 5 segundos.

  3. Fallback a cache: si PostgreSQL no está disponible durante la recarga, el router continúa operando con las últimas reglas cacheadas. Un alerta se genera para operaciones.

  4. Auditoría de regla aplicada: cada mensaje enrutado incluye en sus headers el rule_id y rule_version que se aplicaron, permitiendo trazabilidad completa.

  5. Validación en la UI: las reglas se validan antes de persistirse (el canal de destino existe, el tenant_id es válido, no hay conflictos de prioridad).

Riesgos y Mitigaciones

Riesgo Mitigación
Regla inválida en producción Validación en UI + dry-run antes de activar
Inconsistencia durante propagación Swap atómico de cache, ventana < 5 segundos
Almacén no disponible Fallback a cache + alerta operacional
Rollback necesario Versionado de reglas con rollback por versión
Regla huérfana (tenant offboarded) Proceso de cleanup periódico + effective_until

9. Desarrollo Paso a Paso del Ejemplo

Paso 1: Onboarding de Nuevo Tenant

La empresa "NovaCorp" firma contrato para usar la plataforma. El equipo de operaciones:

  1. Abre la Rule Admin UI.
  2. Crea una nueva regla:
  3. tenant_id: TENANT-NOVACORP
  4. condition: header.tenant_id == 'TENANT-NOVACORP'
  5. destination: pipeline.cxml-rest-oracle
  6. priority: 100
  7. effective_from: 2026-04-07T00:00:00Z
  8. La UI valida que el canal pipeline.cxml-rest-oracle existe y tiene consumidores activos.
  9. La UI persiste la regla en PostgreSQL (versión 1) y publica el evento RoutingRuleChanged.

Paso 2: Propagación de la Nueva Regla

Las 4 instancias del Dynamic Router reciben el evento RoutingRuleChanged:

  1. Cada instancia consulta PostgreSQL para obtener el conjunto completo de reglas activas (201 reglas ahora).
  2. Cada instancia construye un nuevo HashMap tenant_id → destination.
  3. Cada instancia hace un swap atómico de la referencia del HashMap.
  4. Latencia de propagación total: 3.2 segundos (evento + query + swap).

Paso 3: Routing del Primer Documento de NovaCorp

NovaCorp envía su primera factura electrónica:

{
  "headers": {
    "message_id": "msg-2026-04-07-nova-001",
    "tenant_id": "TENANT-NOVACORP",
    "document_type": "INVOICE",
    "format": "cXML",
    "timestamp": "2026-04-07T10:15:00Z"
  },
  "body": { "...cXML invoice content..." }
}

El Dynamic Router: 1. Consume el mensaje del canal documents.intake. 2. Lee el header tenant_id: TENANT-NOVACORP. 3. Busca en el cache: TENANT-NOVACORP → pipeline.cxml-rest-oracle. MATCH. 4. Produce el mensaje al canal pipeline.cxml-rest-oracle. 5. Añade headers de auditoría: routing_rule_id: rule-nova-001, routing_rule_version: 1. 6. Hace ack del mensaje de entrada. 7. Latencia de routing: 0.8ms.

Paso 4: Cambio de Configuración de Tenant Existente

TENANT-ACME migra de SAP a Oracle. El equipo de operaciones:

  1. Edita la regla de TENANT-ACME en la Rule Admin UI.
  2. Cambia destination de pipeline.ubl-rest-sap a pipeline.ubl-rest-oracle.
  3. La UI persiste la actualización (versión 2) y publica RoutingRuleChanged.
  4. Las instancias del router actualizan su cache en ~3 segundos.
  5. A partir de este punto, los documentos de ACME se dirigen al nuevo pipeline.

No hubo redespliegue. No hubo interrupción. No se afectó a ningún otro tenant.

Paso 5: Offboarding de Tenant

TENANT-GLOBEX termina contrato:

  1. El operador desactiva la regla de GLOBEX (active: false, effective_until: now).
  2. Las instancias del router actualizan cache.
  3. Si GLOBEX envía un documento después del offboarding, no coincide con ninguna regla activa → va al canal pipeline.unroutable.
  4. El Unroutable Handler genera una notificación: "documento recibido de tenant inactivo GLOBEX".

Paso 6: Monitoreo del Dynamic Router

El dashboard muestra:

  • Reglas activas: 200 (tras offboarding de GLOBEX).
  • Última actualización de cache: hace 2 minutos.
  • Throughput por tenant: top 10 tenants por volumen.
  • Tasa de unroutable: 0.02% (normal).
  • Latencia de routing: p50=0.5ms, p99=3.1ms.
  • Latencia de propagación de última regla: 3.2 segundos.

10. Diagrama Técnico del Patrón

Código Python con diagrams

Diagrama General

Diagrama AWS

Diagrama Azure

Ver / Copiar código de los diagramas
from diagrams import Diagram, Cluster, Edge
from diagrams.onprem.queue import Kafka
from diagrams.onprem.compute import Server
from diagrams.onprem.database import PostgreSQL
from diagrams.onprem.inmemory import Redis
from diagrams.onprem.monitoring import Grafana
from diagrams.onprem.client import User

with Diagram("Dynamic Router - SaaS B2B Document Routing", show=False, direction="LR"):

    with Cluster("Rule Management"):
        admin = User("Operations\nTeam")
        admin_ui = Server("Rule\nAdmin UI")
        rule_db = PostgreSQL("Rule Store\n(PostgreSQL)")

    with Cluster("Control Plane"):
        control_bus = Kafka("control.routing\n.rules.changed")

    with Cluster("Document Intake"):
        intake = Server("Document\nIntake Service")
        input_ch = Kafka("documents.intake")

    with Cluster("Dynamic Router (4 instances)"):
        router = Server("Dynamic\nRouter")
        cache = Redis("Rule Cache\n(In-Memory)")

    with Cluster("Tenant Pipelines"):
        pipe_ubl = Kafka("pipeline.ubl\n-rest-oracle")
        pipe_edi = Kafka("pipeline.edifact\n-sftp-legacy")
        pipe_fact = Kafka("pipeline.facturae\n-soap-aeat")
        pipe_cxml = Kafka("pipeline.cxml\n-rest-oracle")
        pipe_unroutable = Kafka("pipeline\n.unroutable")

    with Cluster("Processors"):
        proc_ubl = Server("UBL-REST\nProcessor")
        proc_edi = Server("EDIFACT-SFTP\nProcessor")
        proc_fact = Server("Facturae-SOAP\nProcessor")
        proc_cxml = Server("cXML-REST\nProcessor")
        proc_unroutable = Server("Unroutable\nHandler")

    monitoring = Grafana("Router\nMonitoring")

    # Rule management flow
    admin >> admin_ui >> rule_db
    admin_ui >> control_bus

    # Rule propagation
    control_bus >> router
    router >> Edge(style="dashed", label="reload") >> rule_db
    rule_db >> Edge(style="dashed") >> cache

    # Message flow
    intake >> input_ch >> router
    router >> pipe_ubl >> proc_ubl
    router >> pipe_edi >> proc_edi
    router >> pipe_fact >> proc_fact
    router >> pipe_cxml >> proc_cxml
    router >> Edge(style="dashed", label="default") >> pipe_unroutable >> proc_unroutable

    # Monitoring
    router >> Edge(style="dotted") >> monitoring
from diagrams import Diagram, Cluster, Edge
from diagrams.aws.compute import Lambda
from diagrams.aws.database import Dynamodb, ElasticacheForRedis
from diagrams.aws.integration import SQS, Eventbridge
from diagrams.aws.management import Cloudwatch, SystemsManager


with Diagram("Dynamic Router - SaaS B2B Document Routing (AWS)", show=False, direction="LR"):

    with Cluster("Rule Management"):
        admin_ui = Lambda("Rule\nAdmin UI")
        rule_db = Dynamodb("Dynamodb\nRule Store")
        appconfig = SystemsManager("AppConfig\n(Feature Flags)")

    with Cluster("Control Plane"):
        control_bus = Eventbridge("EventBridge\nrules.changed")

    with Cluster("Document Intake"):
        intake = Lambda("Document\nIntake Service")
        input_ch = SQS("documents.intake")

    with Cluster("Dynamic Router (4 instances)"):
        router = Lambda("Dynamic\nRouter")
        cache = ElasticacheForRedis("ElastiCache\nRule Cache")

    with Cluster("Tenant Pipelines"):
        pipe_ubl = SQS("pipeline.ubl\n-rest-oracle")
        pipe_edi = SQS("pipeline.edifact\n-sftp-legacy")
        pipe_fact = SQS("pipeline.facturae\n-soap-aeat")
        pipe_cxml = SQS("pipeline.cxml\n-rest-oracle")
        pipe_unroutable = SQS("pipeline\n.unroutable")

    with Cluster("Processors"):
        proc_ubl = Lambda("UBL-REST\nProcessor")
        proc_edi = Lambda("EDIFACT-SFTP\nProcessor")
        proc_fact = Lambda("Facturae-SOAP\nProcessor")
        proc_cxml = Lambda("cXML-REST\nProcessor")
        proc_unroutable = Lambda("Unroutable\nHandler")

    monitoring = Cloudwatch("Router\nMonitoring")

    # Rule management flow
    admin_ui >> rule_db
    admin_ui >> control_bus

    # Rule propagation
    control_bus >> router
    router >> Edge(style="dashed", label="reload") >> rule_db
    rule_db >> Edge(style="dashed") >> cache
    router >> Edge(style="dashed", label="config") >> appconfig

    # Message flow
    intake >> input_ch >> router
    router >> pipe_ubl >> proc_ubl
    router >> pipe_edi >> proc_edi
    router >> pipe_fact >> proc_fact
    router >> pipe_cxml >> proc_cxml
    router >> Edge(style="dashed", label="default") >> pipe_unroutable >> proc_unroutable

    # Monitoring
    router >> Edge(style="dotted") >> monitoring
from diagrams import Diagram, Cluster, Edge
from diagrams.onprem.client import User
from diagrams.azure.compute import FunctionApps
from diagrams.azure.database import CacheForRedis, SQLServers
from diagrams.azure.devops import ApplicationInsights
from diagrams.azure.integration import ServiceBus


with Diagram("Dynamic Router - SaaS B2B Document Routing (Azure)", show=False, direction="LR"):

    with Cluster("Rule Management"):
        admin = User("Operations\nTeam")
        admin_ui = FunctionApps("Rule\nAdmin UI")
        rule_db = SQLServers("Azure SQL\n(Rule Store)")

    with Cluster("Control Plane"):
        control_bus = ServiceBus("control.routing\n.rules.changed\n(Topic)")

    with Cluster("Document Intake"):
        intake = FunctionApps("Document\nIntake Service")
        input_ch = ServiceBus("documents.intake\n(Queue)")

    with Cluster("Dynamic Router + App Configuration"):
        router = FunctionApps("Dynamic\nRouter")
        cache = CacheForRedis("Azure Redis\nCache (Rules)")

    with Cluster("Service Bus Tenant Queues"):
        pipe_ubl = ServiceBus("pipeline.ubl\n-rest-oracle")
        pipe_edi = ServiceBus("pipeline.edifact\n-sftp-legacy")
        pipe_fact = ServiceBus("pipeline.facturae\n-soap-aeat")
        pipe_cxml = ServiceBus("pipeline.cxml\n-rest-oracle")
        pipe_unroutable = ServiceBus("pipeline\n.unroutable")

    with Cluster("Processors"):
        proc_ubl = FunctionApps("UBL-REST\nProcessor")
        proc_edi = FunctionApps("EDIFACT-SFTP\nProcessor")
        proc_fact = FunctionApps("Facturae-SOAP\nProcessor")
        proc_cxml = FunctionApps("cXML-REST\nProcessor")
        proc_unroutable = FunctionApps("Unroutable\nHandler")

    monitoring = ApplicationInsights("Application\nInsights")

    # Rule management flow
    admin >> admin_ui >> rule_db
    admin_ui >> control_bus

    # Rule propagation
    control_bus >> router
    router >> Edge(style="dashed", label="reload") >> rule_db
    rule_db >> Edge(style="dashed") >> cache

    # Message flow
    intake >> input_ch >> router
    router >> pipe_ubl >> proc_ubl
    router >> pipe_edi >> proc_edi
    router >> pipe_fact >> proc_fact
    router >> pipe_cxml >> proc_cxml
    router >> Edge(style="dashed", label="default") >> pipe_unroutable >> proc_unroutable

    # Monitoring
    router >> Edge(style="dotted") >> monitoring

Explicación del Diagrama

El diagrama muestra los dos planos del Dynamic Router:

  1. Control Plane (gestión de reglas): el equipo de operaciones gestiona reglas en la Rule Admin UI, que las persiste en PostgreSQL y publica eventos de cambio en el canal de control. El router recibe los eventos, recarga reglas de PostgreSQL y actualiza su cache.
  2. Data Plane (flujo de mensajes): los documentos entran por el Document Intake Service, pasan por el canal de entrada, son evaluados por el Dynamic Router contra el cache de reglas y se dirigen al pipeline de tenant correspondiente.

Correspondencia Patrón ↔ Diagrama

Concepto del Patrón Componente del Diagrama
Productor Document Intake Service
Canal de Entrada documents.intake
Dynamic Router Dynamic Router (4 instancias)
Almacén de Reglas PostgreSQL Rule Store
Cache de Reglas In-Memory Rule Cache
Canal de Control control.routing.rules.changed
Canales de Salida Tenant pipelines
Ruta por Defecto pipeline.unroutable
Admin de Reglas Operations Team + Rule Admin UI

11. Beneficios

Impacto Técnico

  • Zero-downtime routing changes: las reglas se actualizan sin interrupción del servicio de routing.
  • Propagación rápida: cambios de reglas se reflejan en segundos, no en minutos u horas de despliegue.
  • Desacoplamiento de ciclos de release: el routing evoluciona independientemente del código del router.
  • Escalabilidad de configuración: 200 o 2,000 tenants con reglas individuales se gestionan con el mismo mecanismo.

Impacto Organizacional

  • Self-service para operaciones: el equipo de operaciones gestiona el routing sin depender del equipo de desarrollo.
  • Onboarding acelerado: un nuevo tenant se onboardea en minutos (crear regla) en lugar de días (ciclo de deploy).
  • Separación de responsabilidades: desarrollo gestiona el código del router; operaciones gestiona las reglas.

Impacto Operacional

  • Respuesta rápida a cambios regulatorios: una nueva regulación que requiere routing diferente se implementa en minutos.
  • A/B testing operacional: dirigir un porcentaje del tráfico de un tenant a un nuevo pipeline se configura como una regla con peso.
  • Auditoría completa: cada documento incluye la referencia a la regla que lo enrutó, permitiendo trazabilidad retroactiva.

Beneficios de Mantenibilidad y Evolución

  • Reglas versionadas: el historial de cambios de reglas permite auditoría y rollback.
  • Testing de reglas en aislamiento: una nueva regla se puede probar con tráfico sintético antes de activarla para tráfico real.
  • Evolución incremental: nuevos tipos de condiciones o destinos se añaden al almacén de reglas sin cambiar el código del router.

12. Desventajas y Riesgos

Complejidad Añadida

  • Almacén externo: introduce una dependencia operacional adicional (PostgreSQL, config store) que debe ser altamente disponible.
  • Mecanismo de sincronización: polling, watches o eventos de control añaden complejidad a la infraestructura.
  • Cache invalidation: uno de los "dos problemas difíciles en computer science" está en el corazón del Dynamic Router.
  • Validación de reglas: las reglas que antes se validaban en tiempo de compilación ahora se validan en runtime, con mayor riesgo de errores.

Riesgos de Mal Uso

  • Reglas como lógica de negocio: si las reglas del Dynamic Router se vuelven tan complejas que implementan lógica de negocio (transformaciones, condicionales anidados, lookups), el almacén de reglas se convierte en un motor de reglas no gestionado.
  • Demasiada flexibilidad: dar a los tenants self-service para definir sus reglas de routing puede producir configuraciones inválidas, conflictivas o que referencian destinos inexistentes.
  • Proliferación de reglas: sin cleanup, las reglas de tenants offboarded permanecen en el almacén indefinidamente, dificultando la gestión.

Sobreingeniería

  • Dynamic Router para reglas estáticas: si las reglas cambian una vez al año, la complejidad de un almacén de reglas con sincronización y cache es injustificada. Un Content-Based Router con CI/CD es suficiente.
  • Framework de reglas genérico: construir un motor de reglas genérico y extensible cuando las reglas son siempre igualdades simples (tenant_id == X → destination Y).

Costos de Operación

  • Infraestructura adicional: almacén de reglas, mecanismo de propagación, UI de administración.
  • Monitoreo adicional: disponibilidad del almacén, latencia de propagación, consistencia de cache entre instancias.
  • Gobierno de reglas: proceso para crear, revisar, aprobar y auditar cambios de reglas.

Anti-Patterns Relacionados

  • Rule Store as God Object: almacenar todas las configuraciones de la plataforma (no solo routing) en el almacén de reglas, convirtiéndolo en un single point of failure.
  • Unversioned Rules: modificar reglas sin versionado, haciendo imposible saber qué regla estaba activa cuando un documento se enrutó incorrectamente hace una semana.

13. Relación con Otros Patrones

Patrones Complementarios

  • Content-Based Router (este capítulo): Dynamic Router es la evolución dinámica de Content-Based Router. Comparten la lógica de evaluación; difieren en la fuente de las reglas.
  • Control Bus (Capítulo 3): el canal de control que notifica cambios de reglas al router es una implementación directa de Control Bus.
  • Message Channel (Capítulo 2): cada destino referenciado por una regla es un Message Channel.
  • Message Store (Capítulo 2): el almacén de reglas funciona conceptualmente como un Message Store para la configuración del router.

Patrones que Suelen Aparecer Antes o Después

  • Antes: Message Translator — normaliza mensajes a un formato que el router pueda evaluar.
  • Antes: Channel Adapter — adapta protocolos de ingreso (SFTP, AS2, email) a mensajes en el canal de entrada.
  • Después: procesadores especializados por tenant/pipeline.

Combinaciones Comunes

  • Dynamic Router + Recipient List: el Dynamic Router selecciona el destino primario; la Recipient List distribuye a destinos secundarios (auditoría, compliance).
  • Dynamic Router + Feature Flags: las reglas de routing se vinculan a feature flags para habilitar/deshabilitar rutas basándose en el estado del flag.
  • Dynamic Router + Circuit Breaker: si un destino no está disponible, el circuit breaker desactiva la regla temporalmente y activa una ruta de fallback.

Diferencias con Patrones Similares

  • vs. Content-Based Router: reglas estáticas vs. dinámicas. La diferencia es operacional, no conceptual.
  • vs. Routing Slip: el Dynamic Router decide el destino en el router; el Routing Slip lleva la ruta dentro del mensaje.
  • vs. Message Broker: el Message Broker es una plataforma completa de routing; el Dynamic Router es un componente dentro de una plataforma.
  • vs. Recipient List: el Dynamic Router envía a un destino; la Recipient List envía a múltiples destinos.

Encaje en un Flujo Mayor de Integración

Dynamic Router se ubica como el componente central de routing en plataformas donde la topología es configurable. En una plataforma SaaS, es el punto de decisión entre la ingestión unificada y el procesamiento por tenant. En un sistema con feature flags, es el punto donde se bifurca el tráfico entre la versión estable y la experimental.


14. Relevancia Actual del Patrón

Evaluación: Relevancia Alta

Argumentación

Dynamic Router es uno de los patrones de mayor relevancia actual por la confluencia de varias tendencias:

  • SaaS multi-tenant: toda plataforma SaaS necesita routing por tenant, y los tenants cambian frecuentemente.
  • Feature flags: plataformas como LaunchDarkly, Split.io y Unleash implementan routing dinámico a nivel de aplicación. A nivel de mensajería, el concepto es idéntico.
  • GitOps y configuration-as-code: herramientas como ArgoCD, Flux y Helm values permiten cambiar configuración sin ciclos de deploy convencionales.
  • API management: plataformas como Kong, Apigee y AWS API Gateway permiten routing dinámico de requests HTTP basado en configuración que se modifica en runtime.
  • Cloud-native configuration: AWS AppConfig, Azure App Configuration, Consul KV, etcd son almacenes de configuración diseñados para exactly este caso de uso.

Cómo Se Implementa Hoy

Plataforma Implementación Mecanismo de Actualización
AWS AppConfig Feature flags + configuration Deployment strategy (rolling, canary)
Azure App Configuration Key-value con labels Event Grid notification
Consul KV Key-value distribuido Watch mechanism
etcd Key-value distribuido Watch API
LaunchDarkly Feature flags Streaming updates (SSE)
Kong Route configuration Admin API (runtime)
Apache Camel Dynamic Router EIP Refreshable route definitions
Spring Cloud Config Centralized config Bus refresh (@RefreshScope)

Qué Parte Sigue Siendo Esencial

  • El concepto de separar las reglas de routing del código del router para permitir cambios sin redespliegue.
  • La necesidad de un mecanismo confiable de propagación de cambios a todas las instancias del router.
  • La importancia de versionado y rollback de reglas.
  • La distinción entre control plane (gestión de reglas) y data plane (evaluación de mensajes) como principio de diseño.

15. Implementación en Arquitecturas Modernas

AWS AppConfig + Lambda

import boto3
import json

appconfig = boto3.client('appconfigdata')

# Start configuration session
session = appconfig.start_configuration_session(
    ApplicationIdentifier='document-platform',
    EnvironmentIdentifier='production',
    ConfigurationProfileIdentifier='routing-rules'
)

# Get latest configuration (cached, polled periodically)
config = appconfig.get_latest_configuration(
    ConfigurationToken=session['InitialConfigurationToken']
)
routing_rules = json.loads(config['Configuration'].read())

def route_document(event):
    tenant_id = event['headers']['tenant_id']
    rule = routing_rules.get(tenant_id, routing_rules['DEFAULT'])
    return rule['destination']

Spring Cloud + Consul KV

@RefreshScope
@Component
public class DynamicDocumentRouter {

    @Value("${routing.rules}")
    private Map<String, String> routingRules;

    public String route(Message<?> message) {
        String tenantId = message.getHeaders().get("tenant_id", String.class);
        return routingRules.getOrDefault(tenantId, "pipeline.unroutable");
    }
}

Con Consul KV y Spring Cloud Consul, un cambio en config/document-router/routing.rules se propaga automáticamente a todas las instancias via @RefreshScope.

Kafka Streams con External Rule Store

KStream<String, Document> documents = builder.stream("documents.intake");

// Rule store se consulta via GlobalKTable o State Store externo
GlobalKTable<String, RoutingRule> rules = builder.globalTable("routing.rules");

documents
    .join(rules,
        (key, doc) -> doc.getTenantId(),
        (doc, rule) -> new RoutedDocument(doc, rule.getDestination()))
    .to((key, value, context) -> value.getDestination(),
        Produced.with(stringSerde, routedDocSerde));

Azure App Configuration + Event Grid

1. Operador actualiza regla en Azure App Configuration
2. Azure App Configuration emite evento a Event Grid
3. Event Grid trigger notifica al Dynamic Router (Azure Function)
4. Router recarga configuración desde App Configuration
5. Próximo mensaje se evalúa con la nueva regla

16. Consideraciones de Gobierno y Operación

Observabilidad

  • Métricas del router: throughput por regla, latencia de routing, tasa de unroutable, cache hit rate.
  • Métricas de reglas: número de reglas activas, fecha de última actualización, versión actual.
  • Métricas de propagación: latencia entre cambio de regla y aplicación efectiva en todas las instancias.
  • Auditoría de routing: cada mensaje enrutado registra rule_id y rule_version en headers para trazabilidad.

Monitoreo

  • Disponibilidad del almacén de reglas: si el almacén no está disponible, el router opera con cache pero no puede actualizar reglas.
  • Consistencia entre instancias: verificar que todas las instancias del router tienen la misma versión de reglas.
  • Antigüedad del cache: alertar si el cache no se ha refrescado en más de N minutos.
  • Tasa de unroutable: incrementos indican reglas faltantes o tenants no configurados.

Versionado

  • Versionado de reglas: cada cambio incrementa la versión. El historial completo se retiene para auditoría.
  • Rollback: capacidad de revertir a una versión anterior de las reglas en caso de error.
  • Changelog: registro de quién cambió qué, cuándo y por qué.

Seguridad

  • Control de acceso al almacén: RBAC para quién puede crear, modificar, eliminar y visualizar reglas.
  • Separación de environments: reglas de staging y producción en almacenes separados.
  • Validación antes de activación: las reglas se validan antes de persistirse (destino existe, condición es parseable, no hay conflictos).

Manejo de Errores

  • Almacén no disponible: el router opera con cache, alerta operacional, no interrumpe el routing.
  • Regla inválida en cache: si una regla causa error en evaluación, el mensaje va a unroutable y se genera alerta.
  • Destino no disponible: si el canal destino de una regla no está disponible, dead-letter + alerta.

Performance

  • Cache en memoria: las reglas se evalúan desde un HashMap en memoria, no desde red.
  • Recarga incremental: si es posible, recargar solo las reglas cambiadas, no el conjunto completo.
  • Swap atómico: la actualización del cache debe ser atómica para evitar estados intermedios.

Escalabilidad

  • El Dynamic Router escala horizontalmente como un Content-Based Router (stateless por mensaje, consumer group compartido).
  • El almacén de reglas debe escalar para lecturas frecuentes (cache es la capa de escalabilidad principal).

17. Errores Comunes

No Implementar Fallback Cuando el Almacén No Está Disponible

Si el almacén de reglas no está disponible y el router no tiene cache o no sabe operar sin el almacén, todo el routing se detiene. El fallback a las últimas reglas conocidas (cache) es obligatorio para resiliencia.

Cache Sin Invalidación

Un cache de reglas con TTL largo (horas) que no responde a eventos de cambio produce un desfase entre la configuración deseada (almacén) y la aplicada (cache). Los operadores cambian una regla y el cambio no se refleja hasta que el TTL expira, causando confusión y desconfianza en el sistema.

Reglas Sin Versionado

Sin versionado, es imposible saber qué regla estaba activa cuando un documento se enrutó incorrectamente la semana pasada. La auditoría retroactiva y el debugging de routing incorrecto requieren que cada regla tenga versión y que cada mensaje registre qué versión de regla se le aplicó.

Validación Insuficiente de Reglas

Permitir que se persistan reglas que referencian destinos inexistentes, tienen condiciones mal formadas o conflictos de prioridad traslada la detección de errores al runtime, donde el impacto es real y directo: documentos mal enrutados o perdidos.

Propagación Lenta Sin Alertas

Si la latencia de propagación de reglas es de 10 minutos (por polling con TTL largo) y el operador no lo sabe, puede crear una regla para un nuevo tenant y asumir que está activa, cuando en realidad los documentos del nuevo tenant aún van a unroutable durante los próximos 10 minutos.

Confundir Dynamic Router con Rules Engine

El Dynamic Router decide a dónde va un mensaje. Si las "reglas" empiezan a incluir transformaciones, validaciones, lookups externos y condicionales anidados, se está construyendo un rules engine, no un Dynamic Router. La responsabilidad del router es routing; la lógica de negocio pertenece a los procesadores.


18. Conclusión Técnica

Dynamic Router es la evolución natural de Content-Based Router para entornos donde la topología de routing es dinámica y configurable. Su concepto — separar las reglas de routing del código del router y almacenarlas en un repositorio externo modificable en runtime — es un principio arquitectónico fundamental para plataformas multi-tenant, sistemas con feature flags y cualquier arquitectura donde los destinos de routing cambian más rápido que los ciclos de despliegue.

Cuándo aporta valor: cuando las reglas de routing cambian con frecuencia suficiente (semanal o más) para que los ciclos de despliegue sean un cuello de botella operacional. Cuando se necesita que personal no desarrollador (operaciones, negocio) pueda gestionar el routing. Cuando la plataforma es multi-tenant con onboarding/offboarding continuo.

Cuándo evita problemas importantes: un Dynamic Router bien implementado — con almacén de reglas versionado, cache con invalidación por evento, validación de reglas antes de activación y fallback a cache cuando el almacén no está disponible — evita los problemas más costosos: ciclos de despliegue para cambios rutinarios de routing, riesgo de regresión en tenants existentes por cambios para nuevos tenants, e incapacidad de responder rápidamente a cambios de negocio o regulatorios.

Cuándo no conviene adoptarlo: si las reglas de routing son estables y cambian pocas veces al año, la complejidad del almacén de reglas externo, la propagación y el cache no se justifican. Un Content-Based Router con reglas en configuración estática y un pipeline de CI/CD ofrece el mismo resultado con menos complejidad.

Recomendación para arquitectos: diseñe el Dynamic Router con una separación clara entre control plane (gestión de reglas) y data plane (evaluación de mensajes). Use un almacén de reglas versionado con validación antes de activación. Implemente cache con invalidación por evento (no solo TTL). Incluya auditoría de qué regla se aplicó a cada mensaje. Y mantenga la disciplina de que el router solo rutea — no transforma, no valida, no enriquece. Si necesita esas funciones, combínelo con los patrones apropiados (Message Translator, Content Enricher, Message Filter) como componentes separados en el pipeline.