Arquitectura (IT)Arquitecto de Sistemas

¿Cómo arquitectarías la sincronización de datos sin tiempo de inactividad entre una base de datos monolítica heredada y un ecosistema de microservicios distribuidos, manteniendo las propiedades ACID durante una migración por fases?

Supere entrevistas con el asistente de IA Hintsage

Respuesta a la pregunta

Historia de la pregunta

La evolución de arquitecturas monolíticas a microservicios ha creado una necesidad crítica de estrategias de migración incrementales. Las organizaciones no pueden permitirse el lujo de una migración completa y detener todo, especialmente aquellas que operan a gran escala con sistemas heredados de Oracle o SQL Server. Esta pregunta surgió de escenarios del mundo real donde las empresas necesitaban modernizarse sin sacrificar años de integridad de datos históricos o aceptar ventanas de mantenimiento que duraban horas.

El problema

El desafío principal radica en la incompatibilidad entre las transacciones ACID monolíticas que abarcan múltiples dominios y la naturaleza distribuida de los microservicios. Al descomponer una base de datos, se enfrenta al escenario de cerebro dividido donde las actualizaciones ocurren en el sistema heredado y en los nuevos servicios simultáneamente. Mantener la integridad referencial a través de límites de red mientras se mantienen ambos sistemas operativos crea un problema de consenso distribuido que no puede resolverse con una simple replicación de base de datos.

La solución

Implementar una Arquitectura Impulsada por Eventos utilizando Captura de Datos de Cambio (CDC) con un Patrón de Outbox para asegurar la publicación confiable de eventos. Desplegar conectores Debezium para capturar cambios a nivel de fila del registro de transacciones de la base de datos heredada, transmitiendo eventos a Apache Kafka como el sistema nervioso central. Simultáneamente, implementar el Patrón Saga en la capa de microservicios para manejar transacciones distribuidas, asegurando consistencia eventual mientras se mantiene la autonomía operativa de cada servicio.

Situación de la vida real

Una plataforma de comercio electrónico de Fortune 500 necesitaba migrar su sistema de gestión de pedidos de un monolito heredado de Oracle de hace diez años a microservicios basados en PostgreSQL. Los módulos de inventario, precios y cumplimiento de pedidos estaban estrechamente acoplados con restricciones de clave foránea a través de doce tablas principales. Durante las temporadas navideñas, el sistema procesaba 50,000 transacciones por minuto sin tolerancia a la pérdida de datos o tiempo de inactividad.

Solución A: Estrategia de Escritura Dual

El equipo de ingeniería inicialmente consideró modificar el código de la aplicación heredada para escribir simultáneamente en Oracle y los nuevos servicios de PostgreSQL. Este enfoque prometía simplicidad al mantener las escrituras sincrónicas y consistentes. Sin embargo, introdujo riesgos de acoplamiento catastróficos: si el nuevo servicio experimentaba latencia o fallos, todo el sistema heredado fallaría. Además, implementar transacciones distribuidas a través del protocolo XA degradaría severamente el rendimiento, aumentando potencialmente los tiempos de respuesta en un 400% durante la carga máxima.

Solución B: Disparadores y Vistas de Base de Datos

Otra opción involucraba crear disparadores en la base de datos en Oracle que invocarían directamente puntos finales REST al modificar filas. Esto parecía atractivo porque no requería cambios en la aplicación. Sin embargo, esto creaba un acoplamiento estrecho entre la infraestructura de la base de datos y la topología de la red, haciendo que el sistema fuera frágil. Si el punto final del microservicio era inaccesible, el disparador fallaría, haciendo que toda la transacción heredada se revirtiera, lo que violaba el requisito de cero tiempo de inactividad. Además, gestionar migraciones de esquemas se volvía casi imposible cuando los disparadores dependían de estructuras de columna específicas.

Solución C: Captura de Datos de Cambio con Event Sourcing

La arquitectura elegida aprovechó Debezium para monitorear el registro de rehacer de Oracle, capturando cada inserción, actualización y eliminación como eventos inmutables publicados en Apache Kafka. Los microservicios consumieron estos eventos a través de Kafka Streams, transformándolos y persistiendo en PostgreSQL utilizando el Patrón de Outbox para asegurar la semántica de exactamente una vez. Un Registro de Esquema gestionado por Confluent hizo cumplir la compatibilidad hacia atrás y hacia adelante utilizando esquemas Avro. Esto desacopló el sistema heredado de la complejidad de la migración - Oracle permaneció ajeno a la nueva arquitectura mientras los servicios consumían eventos a su propio ritmo.

Solución elegida y justificación

El equipo seleccionó la Solución C porque respetaba el Principio de Responsabilidad Única y proporcionaba aislamiento ante fallos. A diferencia de las escrituras duales, el rendimiento del sistema heredado no se vio afectado por la latencia de los microservicios. En comparación con los disparadores, Debezium operó de manera asíncrona sin bloquear transacciones. El registro de eventos proporcionó un rastro de auditoría inmutable, y las políticas de retención de Kafka permitieron reproducir datos históricos si los microservicios necesitaban reprocesarse durante la evolución del esquema.

Resultado

Después de una migración de ocho meses, la plataforma movió con éxito 200 TB de datos transaccionales con un 99.97% de tiempo de actividad. El sistema manejó el tráfico del Black Friday con un 40% menos de latencia que el año anterior. Cuando se descubrió un error de cálculo de precios en los nuevos servicios, el equipo reprodujo tres días de eventos desde Kafka sin tocar el sistema heredado de Oracle, corrigiendo 2.3 millones de registros sin tiempo de inactividad. La canalización de CDC ahora sirve como la columna vertebral para análisis en tiempo real usando Apache Flink.

Lo que los candidatos a menudo pasan por alto

¿Cómo manejas la evolución del esquema cuando el monolito cambia su estructura de tabla mientras los microservicios consumen eventos de CDC?

Los candidatos a menudo sugieren congelar el esquema durante la migración, lo cual es poco práctico para negocios ágiles. El enfoque correcto implica implementar el Registro de Esquema de Confluent con esquemas Avro utilizando modos de compatibilidad hacia adelante y hacia atrás. Cuando las tablas de Oracle cambian, el conector Debezium publica eventos con esquemas actualizados, pero el registro hace cumplir las reglas de compatibilidad. Los servicios deberían implementar el patrón Esquema-en-Lectura utilizando las reglas de resolución de Apache Avro - ignorando campos desconocidos y usando valores predeterminados para los que faltan. Además, desplegar un patrón CQRS donde los modelos de lectura puedan evolucionar independientemente del esquema de origen, utilizando transformadores de Kafka Connect para aplanar estructuras anidadas antes de llegar a los puntos finales de consumo.

¿Qué sucede cuando ambos sistemas actualizan la misma entidad simultáneamente durante el período de transición?

Esto crea un escenario de cerebro dividido que las marcas de tiempo simples no pueden resolver. Los arquitectos deben implementar Relojes Vectoriales o CRDTs (Tipos de Datos Replicados Sin Conflicto) para la resolución de conflictos determinística. Desplegar un componente de Sincronización Bidireccional que consuma eventos de microservicios y escriba de nuevo en Oracle utilizando Kafka Connect JDBC Sink, pero con estrictas semánticas de Última Escritura Gana (LWW) basadas en relojes lógicos híbridos.

Más importante aún, implementar límites de Diseño Guiado por el Dominio - durante la migración, asignar propiedad de escritura exclusiva a ya sea el monolito o el microservicio por cada raíz agregada, nunca ambos. Usar Flags de Base de Datos en Oracle para indicar el estado de la migración, dirigiendo el tráfico de escritura en consecuencia a través de una Puerta de Enlace API usando el Patrón Fig Strangler.

Describe el patrón para asegurar la integridad transaccional cuando una operación comercial abarca tanto la base de datos heredada como los nuevos microservicios.

La mayoría de los candidatos sugieren incorrectamente transacciones distribuidas utilizando Compromiso en Dos Fases (2PC) a través de sistemas heterogéneos, lo que crea un acoplamiento frágil y problemas de disponibilidad. La solución adecuada emplea el Patrón Saga con Transacciones Compensatorias. Cuando una acción del usuario requiere actualizaciones tanto en Oracle (heredado) como en PostgreSQL (nuevo), orquestar esto a través de un Orquestador de Saga construido en Camunda o Temporal. El proceso ejecuta transacciones locales secuencialmente: primero actualiza Oracle, luego publica un evento de dominio, luego ejecuta la operación del microservicio. Si algún paso falla, ejecutar transacciones compensatorias: si el compromiso del microservicio falla, desencadenar un evento de reversión que el sistema heredado consume para revertir el cambio en Oracle. Esto mantiene consistencia eventual sin bloquear recursos a través de límites de red.