php doctrine-orm symfony saas multi-tenant

php - Prácticas recomendadas para desarrollar una aplicación multiusuario con Symfony2 y Doctrine2



doctrine-orm saas (6)

¿Por qué no probar diferentes bases de datos para cada cliente para mantener los datos separados y darles un punto de entrada único a su aplicación? Ejemplo: http://client1.project.net, que con el sistema de enrutamiento se asignará a la base de datos del cliente 1. Lo malo de esto: cambios más complejos en la base de datos, porque todas las bases de datos para cada cliente deben actualizarse.

Estoy trabajando en una aplicación que necesita ser compatible con el modelo de múltiples inquilinos. Estoy usando el framework php symfony2 y la doctrina2.

No estoy seguro de cuál es la mejor manera de diseñar este requisito. ¿La funcionalidad ACL de Symfony proporciona una parte de la solución?

¿Qué recomendaciones o ideas podría proporcionar? ¿Hay algunas aplicaciones symfony2 de muestra o aplicaciones de código abierto disponibles que hayan implementado esta estrategia?

Mi primer pensamiento es usar una columna tenant_id en todas las tablas y tener esto relacionado con el objeto de cuenta en la aplicación. Sin embargo, no estoy seguro de si ACL debe ocuparse de lo que quiero hacer, o si todavía eres responsable de todas las consultas contra tus objetos para que no devuelvan datos no autorizados.

Si no estuviera usando Doctrine, podría ser fácil decir simplemente agregar Where tenant_id = @accountid a cada consulta, pero no estoy seguro de que ese sea el enfoque correcto aquí.

Gracias


Creo que, para administrar la multi-base de datos multiusuario con symfony 2/3. Podemos configurar auto_mapping: false para ORM de la doctrina. archivo: config.yml

doctrine: dbal: default_connection: master connections: master: driver: pdo_mysql host: ''%master_database_host%'' port: ''%master_database_port%'' dbname: ''%master_database_name%'' user: ''%master_database_user%'' password: ''%master_database_password%'' charset: UTF8 tenant: driver: pdo_mysql host: ''%tenant_database_host%'' port: ''%tenant_database_port%'' dbname: ''%tenant_database_name%'' user: ''%tenant_database_user%'' password: ''%tenant_database_password%'' charset: UTF8 orm: default_entity_manager: master auto_generate_proxy_classes: "%kernel.debug%" entity_managers: master: connection: master auto_mapping: false mappings: AppBundle: type: yml dir: Resources/master/config/doctrine tenant: connection: tenant auto_mapping: false mappings: AppBundle: type: yml dir: Resources/tenant/config/doctrine

Después de eso, no podemos manejar la conexión de cada inquilino anulando la información de conexión en request_listener como artículo: http://mohdhallal.github.io/blog/2014/09/12/handling-multiple-entity-managers-in-doctrine-the-smart-way/ Espero que esta práctica pueda ayudar a alguien que trabaja con varios inquilinos.

Saludos,

Vuong Nguyen


Esto es algo que también he estado tratando de averiguar. Lo mejor que se me ocurrió (aún no en una implementación, pero en teoría) es lo siguiente: dar a cada inquilino su propio inicio de sesión en la base de datos y utilizar vistas para evitar que vean los datos de otras personas.

Me encontré con este enlace que describe una forma de hacer esto solo para MySQL anterior (no con Symfony / Doctrine).

Básicamente, tiene sus tablas de base de datos reales, pero cada tabla tiene una columna que almacena el nombre del usuario de la base de datos que hizo la fila. Luego se crean vistas que siempre se filtran por esta columna, por lo que cada vez que un usuario inicia sesión en la base de datos (a través de una herramienta de administración o incluso conectándose a través de Symfony / Doctrine), solo se devuelven registros directamente asociados con ellos. Esto le permite mantener los datos "separados", pero aún en una base de datos. Al extraer datos (por ejemplo, para una entidad en Symfony), estás extrayendo datos de una vista filtrada frente a la tabla de base de datos real.

Ahora, esta solución no es exactamente compatible con Symfony / Doctrine. Pude obtener una prueba muy rápida y rudimentaria de esta ejecución antes; Doctrine pudo usar las vistas de la base de datos muy bien (podía insertar, editar y eliminar entradas de una vista sin problemas). Sin embargo, al hacer cosas como crear / actualizar esquemas, no es divertido. Por supuesto, Symfony / Doctrine parece bastante extensible, así que estoy seguro de que hay una manera de automatizarlo, pero este tipo de configuración no es compatible de forma inmediata. Se debería decir a Doctrine que actualice las tablas, siempre agregue la columna para guardar el nombre de usuario en las tablas de entidades que crea, actualice también las vistas, etc. (También necesitará una forma de cargar la configuración de base de datos adecuada en su La aplicación Symfony, principalmente los diferentes inicios de sesión como el servidor y otras cosas serían iguales.) Pero, si esto se puede superar, su propia aplicación podría ejecutar a estos múltiples inquilinos completamente "ignorantes" del hecho de que los datos de otras personas se encuentran en el base de datos.


Hacemos esto con una de nuestras principales soluciones en el trabajo, y es ciertamente posible. Usamos los paquetes de Symfony2 para crear un paquete "base" que luego se extiende por otros paquetes por cliente.

Dicho esto, estamos pensando en dejar de hacer las cosas de esta manera en el futuro. La decisión de convertirnos en multi-inquilinos no fue la correcta para nosotros, ya que muchos de nuestros clientes se sienten incómodos con sus datos en las mismas tablas de base de datos que los datos de todos los demás. Esto es completamente aparte de los problemas potenciales de rendimiento lento a medida que las tablas crecen.

También hemos encontrado que Doctrine 2 tiene algunos problemas bastante serios a menos que se mantenga bajo control. Si bien esto puede ser un efecto secundario de un código mal estructurado y una lógica de base de datos, creo que es un agujero para que un ORM pueda llegar al punto en el que produce un error fatal porque usa demasiada memoria, especialmente cuando La única razón por la que está utilizando tanta memoria es porque está agrupando las consultas SQL para que puedan ser "más eficientes".

Esto es puramente mi opinión, por supuesto :) Lo que no funciona para nosotros puede funcionar para usted, pero creo que sería mejor que mantuviera las bases de datos separadas por cliente, incluso si todas están almacenadas en el mismo. servidor.


Hemos estado haciendo esto desde hace algún tiempo (aunque no con Symfony y doctrine pero los problemas siguen siendo los mismos): comenzamos con una gran base de datos e implementamos un ''identificador de entorno'' por fila en cada una de las tablas. De esta manera, las migraciones de esquemas fueron fáciles: todo el código se unificó, por lo tanto, un cambio de esquema fue un cambio único en el código y el esquema.

Sin embargo, esto conlleva problemas con la velocidad (tablas grandes), la agilidad (mover / hacer copias de seguridad, etc. los conjuntos de datos grandes son mucho más intensivos que los más pequeños) y, por supuesto, los entornos fáciles de romper ya que un solo fallo eliminará todos los conjuntos de datos en el sistema. ..

Luego cambiamos a múltiples bases de datos; Cada entorno tiene su propio esquema. Al utilizar las migraciones provistas con Doctrine (1 en nuestro caso) podemos actualizar rápidamente la aplicación completa o solo un entorno único. Además, la capacidad de mover cambios específicos entre tentantes permite una mejor precisión en la velocidad y la optimización.

Mi consejo sería: crear un solo núcleo que se extienda a los diferentes inquilinos y mantener la configuración de la base de datos personalizada local por tentante. (en una estructura similar a app.ini)

es decir

/ apps/ tentant1/ models/ <--- specific to the tenant libraries/ <--- specific to the tenant config/ app.ini <--- specific configuration tentant2/ /**/ etc core/ models/ <--- system wide models libraries/ <--- system wide libraries (i.e. doctrine) core.ini <--- system wide configuration

Esto podría mantener todo organizado. Incluso estamos llegando a tener la estructura completa del núcleo disponible por tentante. De este modo, se puede anular todos los aspectos del ''núcleo'' específicos del inquilino.


MEJOR construye diferentes avisos en diferentes mentes. Por favor sea más específico para hacer preguntas. Una de las formas de desarrollar un sistema multiusuario es poner una clave primaria común en todas las tablas para construir la relación. El tipo y la naturaleza de la clave principal son confiables para el proyecto.