En el pasado, solía observar un enfoque común en el que los desarrolladores (incluido yo mismo, por supuesto) utilizaban la misma API tanto para las lecturas como para las escrituras en todos los casos. Más aún, con frecuencia dependíamos de la misma fuente de datos, como MySQL/PostgreSQL, para gestionar ambas operaciones.
Esto significa escribir en las mismas columnas y leer desde ellas, lo que a menudo genera dificultades para optimizar los índices en campos que fueron consultados intensamente.
Por ejemplo, frecuentemente nos encontrábamos modificando índices para acomodar nuevos filtros o mejorar el rendimiento de las consultas, y los campos utilizados con operadores como LIKE planteaban desafíos particulares debido a su impacto en el rendimiento.
Estos cambios a menudo conducen a ajustes adicionales en el backend, incluida la modificación de las API para exponer la funcionalidad actualizada, tiempos medidos debido a JOIN adicionales, etc.
Para abordar el desafío de agregar nuevos filtros y cosas en la API, hubo intentos de optimizar el proceso utilizando herramientas y estándares como Apicalypse y, por supuesto, GraphQL .
Estas soluciones tenían como objetivo agilizar la generación de consultas API y reducir el esfuerzo manual necesario para implementar nuevos filtros y funcionalidades, ofreciendo un enfoque más dinámico para gestionar el acceso a los datos, pero tenían una curva de aprendizaje alta.
Con el auge de CQRS (Command Query Responsibility Segregation), comenzó a surgir un nuevo enfoque. Esta mentalidad fomentaba el uso de fuentes separadas para escrituras y lecturas. Las escrituras podían emitir eventos y las lecturas podían crear vistas a partir de esos eventos en lugares dedicados. Incluso si las lecturas y escrituras se administraban dentro de la misma base de datos (pero en tablas diferentes), esta separación trajo consigo beneficios significativos y, por supuesto, permitió deshacerse del segundo desafío: las uniones y las consultas de búsqueda en modelos de dominio, ya que los modelos de lectura suelen tener la forma de JSON desnormalizados.
Sin embargo, esto generó otro problema. Con las lecturas, teníamos que escalar las escrituras, lo que significa que la única razón por la que teníamos que escalar las instancias de nuestra aplicación de X a Y era debido a las lecturas. Este problema se podría mitigar parcialmente con el almacenamiento en caché y, en el mundo de los microservicios, podríamos tener microservicios dedicados para las lecturas.
Pero...
Aun así, esta no era una solución ideal para otros estilos arquitectónicos como los monolitos modulares, donde tal separación podría no alinearse bien con la filosofía de diseño del sistema. Otra cosa era que, cuando la API no funcionaba, todo el producto no funcionaba y, teniendo en cuenta que la mayoría de los productos dependen más de las lecturas que de las escrituras, podría tener un impacto innecesario en el negocio (por supuesto, una separación de la API no funciona ;) )
Entonces, ¿qué sucedería si pudiéramos solicitar esas "vistas", también conocidas como modelos de lectura, directamente sin involucrar a la API ni manejar cargas? Aquí es donde entran en juego soluciones como Meilisearch , AppSearch y otras, que aprovechan un patrón llamado "Valet Key". Al usar este patrón, los frontends pueden acceder directamente a los modelos optimizados para lectura, lo que reduce la dependencia de las API del backend. Por supuesto, el frontend aún tiene que "pedirle" a la API la "Valet key", pero puede almacenar claves en caché, por lo que incluso cuando la API está inactiva, el frontend aún puede comunicarse y mostrar contenido.
Con este enfoque, podemos centrarnos en la base de datos de lectura y no preocuparnos por gestionar el tráfico de lecturas en nuestra API. La "clave de valet" proporcionada al frontend a través de nuestra API está protegida de forma que el frontend no puede modificarla. Incluye filtros e índices predefinidos.
Si el frontend requiere capacidades adicionales, puede solicitarlas a través de la API, donde la API puede validar si se permiten o no. Siguen siendo menos llamadas.
Algunas ventajas que puedo ver son:
Pero siempre hay contras:
Entonces, este enfoque no es una solución milagrosa e introduce su propio conjunto de desafíos, pero si no le molestan las desventajas, entonces un pequeño cambio en el frontend probablemente no requerirá involucrar al equipo backend, agilizando el proceso de desarrollo y mejorando la agilidad general y, por supuesto, la escalabilidad debería ser más fácil.