Introducción
En este artículo, exploraremos cómo implementar la arquitectura multi-tenant utilizando Symfony y Doctrine.
¿Qué es el multi-tenant?
El término multi-tenant se refiere a una arquitectura en la que una aplicación puede servir información a diferentes usuarios,
manteniendo los datos de cada usuario completamente aislados y accesibles solo para ellos. Esta arquitectura presenta varias
ventajas, como un menor costo al atender a varios clientes con una sola instancia, una mayor facilidad de mantenimiento
al solucionar errores de manera consistente en todos los clientes y una mayor seguridad al mantener los datos de cada cliente
separados en bases de datos individuales.
Cómo enfocar la implementación
En primer lugar, os he dejado este repositorio en github con un ejemplo básico para que podáis verlo y no solo leer.
Para implementar el multi-tenant con Symfony y Doctrine, utilizaremos la propiedad wrapper_class
proporcionada por Doctrine.
Esta propiedad nos permite extender la conexión de Doctrine y crear un método para cerrar la conexión actual y abrir
una nueva conexión a otra base de datos. En el archivo config/packages/doctrine.yaml
, podemos configurar la conexión
y especificar la clase de envoltorio (wrapper class):
doctrine:
dbal:
default_connection: default
connections:
default:
url: '%env(resolve:DATABASE_URL)%'
wrapper_class: App\Doctrine\DynamicConnection
La clase DynamicConnection
se encargará del cambio de base de datos y se verá así:
class DynamicConnection extends Doctrine\DBAL\Connection
{
public function swapDatabase(string $urlConnection): void
{
$this->closePreviousConnection();
$params = $this->prepareConnectionParameters($urlConnection);
parent::__construct($params, $this->_driver, $this->_config, $this->_eventManager);
}
...
}
En esta clase, podemos realizar cualquier tratamiento adicional antes de realizar el cambio de conexión, como gestionar transacciones abiertas.
Es importante destacar que en el método swapDatabase
pasamos la cadena de conexión completa y luego extraemos
los parámetros necesarios.
Además, debemos cambiar la conexión según algún parámetro externo, como el token del cliente. Esto se puede hacer utilizando un «subscriber» de Symfony:
class DynamicConnectionSubscriber implements EventSubscriberInterface
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly array $databases,
) {
}
public function onKernelRequest(RequestEvent $event): void
{
$connection = $event->getRequest()->query->get('conn');
/** @var DynamicConnection $databaseConnection */
$databaseConnection = $this->entityManager->getConnection();
$urlConnection = $this->databases[$connection] ?? null;
if ($urlConnection) {
$databaseConnection->swapDatabase($urlConnection);
}
}
}
En este ejemplo, utilizamos el «subscriber» para cambiar la conexión de acuerdo con un parámetro de la consulta llamado conn
.
Sin embargo, lo más común es utilizar el token del cliente para determinar la base de datos a la que se debe acceder, y modificar esa cadena con la información de dicho cliente.
Conclusiones
Esperamos que esta guía te haya ayudado a comprender cómo implementar el multi-tenant utilizando Symfony y Doctrine.
Recuerda que esta arquitectura ofrece numerosas ventajas, pero también es importante considerar otros enfoques
y alternativas según los requisitos de tu proyecto.
Si quieres profundizar en este tema, te recomendamos explorar la documentación oficial de Symfony y Doctrine,
así como compartir tus experiencias y otras alternativas que conozcas.
¡Gracias por leer y esperamos que logres implementar con éxito el multi-tenant en tus proyectos Symfony!
PD: Si tenéis cualquier duda podéis poneros en contacto conmigo enviando un DM por twitter.
0 comentarios