Los datos estaban ahí. El camino hacia ellos no.
Un concesionario de coches maneja muchos datos: inventario en vivo con precios y disponibilidad, especificaciones de vehículos vinculadas a los VIN, valoraciones de vehículos de intercambio que cambian a diario, opciones de financiación, citas de servicio. Esos datos estaban repartidos en vAuto para el inventario, TradePending para las cotizaciones de intercambio, ChromeData para las especificaciones de los vehículos y el propio CRM de cada concesionario. Nada de ello se mostraba al comprador en el momento en que decidía.
La conversación de compra —qué encaja en mi presupuesto, cuánto me darían por mi intercambio, podéis darle servicio la semana que viene— ocurría por teléfono o en el showroom, horas o días después. Los concesionarios perdían clientes potenciales que no conseguían una respuesta lo bastante rápido.
El encargo: poner una interfaz de chat en el sitio web de cada concesionario que pudiera responder a esas preguntas en tiempo real, conectada a los sistemas que ya tenían las respuestas.
Un constructor visual para los concesionarios, un widget de chat para los compradores.
La plataforma tenía dos superficies distintas. Los concesionarios usaban un constructor visual de flujos en la administración —un editor de grafo de tarjetas donde construían flujos de conversación conectando tarjetas de pregunta, ramas de respuesta y tarjetas de acción: mostrar inventario, obtener una cotización de intercambio, reservar una cita de servicio, capturar un lead—. Sin código requerido. Cada concesionario componía su propio bot; la plataforma se encargaba del renderizado y el enrutamiento.
Los compradores veían un widget de chat embebible en el sitio web del concesionario. El widget ejecutaba los flujos configurados, extraía inventario en vivo de vAuto, mostraba valoraciones de intercambio de TradePending en línea cuando un comprador describía su coche actual y capturaba los datos de contacto en formato ADF para el CRM del concesionario. La conversación se moldeaba en torno a lo que el concesionario había configurado y a lo que el comprador preguntaba.
La plataforma era multi-tenant desde el primer día. Cada concesionario tenía su propia configuración de bot, su propio grafo de flujos, su propia conexión de inventario y CRM. Una sola cuenta podía gestionar varios concesionarios. La autenticación, la facturación y la multitenencia vivían en MySQL; todo lo demás vivía en los almacenes que le encajaban.
- F · 01Constructor visual de flujos
- Los concesionarios construían flujos de conversación en un editor de grafo de tarjetas: preguntas, ramas de respuesta, consultas de inventario, cotizaciones de intercambio, captura de leads. Sin código requerido. Cada concesionario configuraba su propio bot.
- F · 02Feed de inventario de vAuto
- Un trabajo de cola respaldado por Redis parseaba las exportaciones FTP de vAuto, actualizaba los datos de producto en MongoDB y reindexaba Elasticsearch por concesionario. Los cambios del feed quedaban aislados en un solo trabajo.
- F · 03Integración de intercambio con TradePending
- El widget de chat obtenía valoraciones de intercambio de TradePending en línea, mostrando una cotización en la conversación cuando un comprador describía su vehículo actual.
- F · 04Exportación de leads ADF
- Los datos de contacto del comprador capturados en el chat se formateaban como XML ADF y se enrutaban al CRM del concesionario. El formato estándar del sector significaba que no había integración a medida por concesionario.
- F · 05Backend de tres bases de datos
- MongoDB para los esquemas en evolución de bot e inventario, MySQL para la autenticación y facturación multi-tenant, Elasticsearch para la búsqueda de inventario por concesionario. Cada almacén era dueño de un trabajo.
- F · 06Suite de tests de automatización de navegador
- Laravel Dusk cubría los flujos completos de conversación de extremo a extremo. Los cambios de tipo de tarjeta y esquema en MongoDB no rompían las rutas del widget; la suite de tests lo detectaba si lo hacían.
Tres bases de datos, cada una ganándose su lugar.
La decisión de ejecutar tres almacenes en paralelo fue deliberada y se defendió desde el primer día. La tentación en un proyecto como este es normalizarlo todo en un solo almacén y aceptar la fricción en los bordes. No lo hicimos.
MongoDB guardaba las configuraciones de bot y los flujos de conversación: tipos de tarjeta, lógica de ramificación, opciones de respuesta rápida, el esquema de visualización de inventario en evolución. Ese esquema cambiaba cada sprint a medida que crecía el conjunto de funciones. Una migración relacional en cada adición de tipo de tarjeta habría estrangulado la entrega. MongoDB absorbió esos cambios sin ceremonia.
MySQL gestionaba todo lo que necesitaba integridad transaccional: autenticación, facturación y fronteras de multitenencia de los concesionarios. No íbamos a dejar la consistencia eventual cerca del control de acceso ni del estado de las suscripciones. Elasticsearch indexaba cada producto de inventario activo por concesionario, enriquecido con los datos del feed de vAuto y las ofertas e interactivos que cada concesionario había configurado. Cuando un comprador preguntaba por un coche concreto en el chat, el widget consultaba Elasticsearch, no una tabla relacional. La calidad de recuperación se ajustaba a lo que el caso de uso necesitaba.
Tres bases de datos no es complejidad: es precisión. Un solo almacén intentando hacer los tres trabajos habría sido la elección compleja.
La integración con vAuto corría como un worker de cola dedicado. vAuto publicaba archivos de inventario por FTP; un trabajo de cola de Laravel respaldado por Redis los recogía, parseaba el feed, actualizaba MongoDB con los nuevos datos de producto y reindexaba en Elasticsearch el inventario afectado. Cada paso estaba aislado. Cuando vAuto cambiaba el formato de un archivo, cambiaba un trabajo, no todo el stack. La suite de tests de automatización de navegador en Laravel Dusk evitaba que los flujos completos de conversación regresaran a medida que evolucionaban los tipos de tarjeta y los esquemas. 1.736 commits a lo largo de aproximadamente dos años. La arquitectura aguantó.
Dos años en producción; cero reescrituras.
La plataforma se publicó para los primeros concesionarios a finales de 2017. El diseño multibase de datos —que había parecido complejidad en el diagrama de arquitectura— demostró su valor en operación. Los fallos quedaron contenidos en su capa. Un fallo de parseo del feed de vAuto no afectaba a la facturación. Una reindexación de Elasticsearch no bloqueaba una conversación de comprador. Cada almacén fallaba del modo en que falla su tipo, y nada más.
La adopción por parte de los concesionarios se mantuvo. Cuando el widget hace preguntas en lugar de mostrar un formulario de contacto, los compradores responden. El flujo de intercambio —preguntar al comprador por su coche actual, obtener una valoración de TradePending, mostrarla en línea— funcionaba sin requerir personal del concesionario.
El proyecto cerró con 708 archivos de código fuente, una suite completa de cobertura de PHPUnit y Dusk, y una capa de datos que el propio equipo del cliente extendió tras la entrega. Nuevos tipos de tarjeta, nuevos conectores de integración: los añadieron sin nosotros. Tres bases de datos haciendo tres trabajos, y ninguna haciendo el de otra.
