Desarrollo de software a medida
Software a medida con Laravel y PostgreSQL
Lo que PostgreSQL da a una construcción con Laravel y MySQL no - una migración que se revierte al fallar, garantías que Eloquent no puede sortear, e informes sin una segunda base de datos.
Laravel funciona con las dos, así que elegir PostgreSQL es una decisión y no un valor por defecto - y conviene ser concreto sobre lo que aporta, porque las comparaciones disponibles hablan de benchmarks y casi ninguna de lo que pasa de verdad un jueves por la tarde durante un despliegue.
Una migración de Laravel fallida se revierte
Esta es la diferencia que cambia el comportamiento de un equipo, y es la que rara vez se menciona.
PostgreSQL aplica los cambios de esquema de forma transaccional. Una migración de seis sentencias que falla en la cuarta deja la base de datos exactamente como estaba. Nada a medias, nada que desenredar, y la migración se puede corregir y volver a ejecutar.
MySQL no. Las tres primeras sentencias quedan aplicadas, la migración se registra como no ejecutada, y ahora alguien tiene que establecer a mano qué mitad existe. En producción, con prisa, con el despliegue a medio terminar.
La consecuencia es cultural antes que técnica. Donde una migración fallida es recuperable, las migraciones se escriben como deberían: pequeñas, concretas, revisadas. Donde una migración fallida es un incidente, se agrupan, se posponen y al final se aplican a mano. Así los esquemas se salen del control de versiones, y es el estado en el que encontramos más a menudo los proyectos heredados. La lectura relacionada es la migración que tiró el sitio.
Restricciones que Eloquent no puede sortear
Una regla impuesta en un modelo se cumple para el código que pasa por el modelo.
No se cumple para una actualización masiva. No se cumple para una llamada del query builder que escribe directamente en la tabla, que toda base de código tiene en algún sitio. No se cumple para el comando de consola que alguien escribió a las nueve de la noche para arreglar un problema de datos, ni para la importación que corre una vez al trimestre y nunca se revisó.
Impuesto por debajo de la aplicación, se cumple en todos los casos. Un índice único resuelve dos peticiones simultáneas por el mismo registro, algo que comprobar antes nunca consigue: la comprobación y el insert son sentencias separadas y el hueco entre ellas es donde aparece el duplicado. Una clave ajena hace imposible una fila huérfana en vez de improbable. Una restricción de comprobación mantiene una cantidad positiva en la fila, no solo en el formulario que la validó la última vez.
PostgreSQL añade dos que importan en la práctica. Una restricción de exclusión rechaza una reserva cuyo rango de fechas se solapa con otra existente del mismo recurso, cerrando una condición de carrera genuinamente difícil de cerrar en PHP. Y un índice parcial cubre solo las filas que merecen cobertura, para que una consulta sobre pedidos abiertos no pague el coste de cinco años de cerrados.
JSONB, y dónde debe empezar la columna
Algunos datos no tienen esquema que diseñar. Una carga de un proveedor que la altera sin avisar. Ajustes por inquilino. Un formulario que construye el propio cliente.
PostgreSQL lo trata bien y no a disgusto: JSONB se almacena de forma compacta, se consulta con operadores reales, se indexa con GIN y se castea limpiamente a un atributo de Eloquent. Es una decisión de diseño razonable, no una retirada.
El declive es gradual y siempre es la misma historia. Añadir una clave a JSON no necesita migración ni atrae revisión, así que ahí empiezan a ir los campos. Al segundo año nadie puede responder una pregunta sobre los datos sin leer PHP, y sacar un campo a una columna significa escribir una migración que recorra tres años de filas con formas inconsistentes.
La prueba que lo evita: si alguna vez vas a filtrar, ordenar, unir o informar por ello, es una columna. Todo lo demás puede vivir en JSONB, y debería.
Informes sin una segunda base de datos
Muchísimas aplicaciones Laravel se traen miles de filas a PHP y las recorren para producir una cifra, porque la consulta que lo haría en SQL parecía desconocida.
Las funciones de ventana convierten totales acumulados, rankings y comparaciones entre periodos en una sola consulta. Las expresiones de tabla común hacen legible un cálculo de varios pasos en vez de anidado. Los lateral joins resuelven la forma de los tres primeros por cliente, que si no se convierte en un bucle N+1. Cada una de ellas sustituye una página de código por una consulta que la base de datos ejecuta en una pasada.
Apuntadas a una réplica de lectura, esos informes se quedan completamente fuera de la carga transaccional. Un almacén aparte es una respuesta real cuando los informes compiten de verdad con producción o abarcan varios sistemas, pero eso debería seguir a una medición y no precederla, y la mayoría de las aplicaciones llegan ahí más tarde de lo que esperan. Ingeniería de base de datos y Eloquent es donde está este trabajo.
Qué cambia de verdad en Eloquent
Honestos con la fricción, porque hay algo.
La comparación de cadenas distingue mayúsculas, así que un where sobre un
correo o una referencia que funcionaba en MySQL puede dejar de coincidir. LIKE
distingue mayúsculas e ILIKE es la forma que no, y eso no es portable: una
consulta escrita para una base de datos necesita atención en la otra.
Las comparaciones son estrictas de tipo. Pasar una cadena donde la columna es un entero da un error en vez de convertir en silencio, que es el comportamiento mejor y aparecerá en el código que dependía de la conversión.
Las claves autoincrementales usan secuencias, lo que importa al sembrar datos con identificadores explícitos y al copiar filas entre entornos. Y todo lo escrito como SQL en bruto es ahora específico de la base de datos, que es un argumento para mantener el SQL en bruto en pocos sitios con nombre en vez de repartido por los modelos.
Ninguna de estas cosas es un obstáculo. Todas son más baratas de conocer en la semana uno que de descubrir en una suite de pruebas que pasa en local y falla en staging.
La forma del trabajo con Laravel y PostgreSQL, y dónde encaja el esquema
En la primera fase, a propósito, junto al flujo alrededor del cual se construye, no en un documento de diseño que la precede ni en una revisión que la sigue.
Cuatro decisiones merecen nombrarse porque, si se posponen, se convierten en migraciones por todas las tablas relacionadas. El conjunto de restricciones y qué hace imposible cada una. La frontera entre una columna y JSONB. Cómo se comportará un cambio de esquema contra una tabla que acabará con cuarenta millones de filas, lo que decide si un índice se crea de forma concurrente o con un bloqueo que nadie presupuestó. Y si los informes leen de una réplica, porque retrofitarlo significa auditar cada consulta para ver qué conexión usó.
El resto del proyecto está estructurado como en la página de software a medida: por fases, cada una acordada por escrito, cada una acabando con algo desplegado.
Qué recibes con un proyecto de Laravel y PostgreSQL
Más allá de la aplicación y su repositorio, las cosas propias de esta elección.
Las migraciones, que son el historial de la base de datos y el motivo de que un cambio de esquema se pueda revisar como cualquier otro código. Una nota sobre el modelo de datos que explique la forma en vez de repetirla. Un inventario de índices que diga para qué consulta existe cada uno, de modo que un futuro desarrollador que quite uno sepa qué se va a volver lento. Las restricciones anotadas con lo que evitan. Y las consultas de informes escritas y explicadas: una función de ventana que nadie entiende es una que se sustituirá por un bucle en PHP la primera vez que haya que cambiarla.
Cuándo MySQL es la mejor respuesta
Si tu hosting, tu equipo de operaciones o la política de tu cliente está construida alrededor de MySQL, eso es una restricción real y no una deficiencia técnica. Un equipo que sabe restaurar, monitorizar y ajustar una base de datos vale más que un conjunto de características marginalmente mejor en otra que nadie ha operado.
Si una aplicación existente ya está en MySQL y está sana, migrarla por las características de arriba rara vez compensa la interrupción por sí sola. Hazlo cuando haya otro motivo para tocar la capa de datos.
Y si la aplicación es pequeña, de lectura mayoritaria y el esquema está asentado, la diferencia entre las dos no será lo que decida si el proyecto sale bien.
