Rejoice Software logo

Laravel · · 4 min de lectura

Laravel multi-tenant: una base de datos o varias en producción

Hemos lanzado ambas opciones. Estos son los fallos típicos, las ventajas e inconvenientes y los caminos de migración que nos habría gustado conocer desde el primer día.

Dos formas de separar clientes

En una aplicación multi-tenant todos los clientes (tenants) comparten el mismo código, pero cada uno solo debe ver sus propios datos. En Laravel hay dos maneras principales de conseguirlo.

En el modelo de base de datos única, cada tabla con datos de cliente tiene una columna tenant_id y cada consulta se limita al tenant actual. En el modelo multi-base de datos, cada tenant tiene su propia base de datos (o esquema) y la aplicación cambia de conexión cuando sabe qué tenant está activo. Ambos funcionan bien en producción; fallan de formas distintas.

Base de datos única: puntos fuertes y riesgos

Una única base de datos es sencilla de operar. Hay un solo juego de migraciones, una copia de seguridad, un pool de conexiones e informes fáciles entre todos los clientes. También es barata: mil tenants pequeños cuestan lo mismo que uno.

El riesgo principal es una fuga de datos. Una consulta que olvida el filtro de tenant —una consulta en bruto, un informe, un job que se ejecuta fuera de una petición— puede devolver datos de otro cliente. Los otros riesgos son los vecinos ruidosos (un tenant grande que ralentiza a todos) y las restauraciones: recuperar los datos de un solo tenant desde una copia compartida es incómodo.

Multi-base de datos: puntos fuertes y riesgos

Las bases de datos separadas ofrecen un aislamiento fuerte. Es mucho menos probable que un error muestre a un tenant los datos de otro, puedes hacer copia y restaurar un solo cliente, ubicar sus datos en una región concreta y llevar un tenant grande a su propio servidor.

El coste es operativo. Cada migración se ejecuta una vez por tenant, así que los despliegues tardan más y un fallo a mitad deja a los tenants con versiones de esquema distintas. Los límites de conexiones se vuelven una restricción real, y la analítica entre tenants necesita una canalización adicional hacia un data warehouse.

Hacerlo bien con base de datos única en Laravel

  • Resuelve el tenant una sola vez y pronto —en un middleware, a partir del dominio, subdominio o usuario autenticado— y guárdalo en un único lugar.
  • Aplica un global scope y asigna tenant_id automáticamente al crear modelos, mediante un trait compartido.
  • Empieza los índices compuestos por tenant_id e incluye tenant_id en las restricciones únicas (por ejemplo tenant_id + email).
  • Pasa el ID del tenant a cada job en cola y restáuralo antes de ejecutarlo.
  • Añade el tenant como prefijo a las claves de caché y a las rutas de almacenamiento de ficheros.
  • Escribe tests que creen dos tenants y comprueben que uno nunca puede leer los registros del otro.
trait BelongsToTenant
{
    protected static function bootBelongsToTenant(): void
    {
        static::addGlobalScope('tenant', fn ($query) => $query->where('tenant_id', tenant()->id));

        static::creating(fn ($model) => $model->tenant_id ??= tenant()->id);
    }
}

Hacerlo bien con varias bases de datos

Mantén una base de datos central para tenants, dominios, planes y facturación, y una base de datos por tenant para sus datos. Cambia la conexión por defecto en el middleware y en los jobs, y restablécela después para que un worker de larga duración nunca se quede con el tenant equivocado.

Ejecuta las migraciones de tenants con un comando que recorra los tenants, registre el progreso y pueda reanudarse tras un fallo. Vigila el límite de conexiones del servidor de base de datos y usa un pooler si tienes muchos workers. Paquetes como stancl/tenancy y spatie/laravel-multitenancy resuelven gran parte de esto y admiten ambos modelos.

El modelo híbrido

Muchos productos maduros acaban en un punto intermedio: la mayoría de los tenants comparten base de datos, mientras que los clientes enterprise que necesitan aislamiento, residencia de datos o rendimiento dedicado tienen la suya. Si cada tabla ya tiene tenant_id, mover un tenant a una base dedicada es una exportación e importación, no una reescritura. Es otra razón para añadir tenant_id desde el primer día, aunque más adelante puedas pasar a varias bases de datos.

Cómo decidir

  • Muchos clientes pequeños y un equipo reducido: base de datos única.
  • Contratos que exigen aislamiento, restauraciones por cliente o ubicaciones de datos concretas: varias bases de datos o modelo híbrido.
  • Pocos clientes muy grandes con cargas intensas: híbrido, con bases de datos dedicadas para ellos.
  • Si dudas: base de datos única con tenant_id en todas partes y tests de aislamiento sólidos; mantiene abiertas todas las opciones.

¿Quieres que revisemos tu web?

Usa el comprobador web gratuito para obtener un informe al instante, o crea un ticket y un ingeniero te responderá en un día laborable.

#Laravel #SaaS #Multi-tenancy

Hablemos

Construyamos algo memorable.

Llamada estratégica gratis de 30 minutos. Sin compromiso.

Empezar un proyecto