Meilleures pratiques pour le développement d'une application multi-locataires avec Symfony2 et Doctrine2
je travaille sur une application qui doit supporter le modèle multi-locataires. J'utilise le cadre et la doctrine2 de symfony2 php.
Je ne suis pas sûr de la meilleure façon de concevoir cette exigence. La fonctionnalité ACL de Symfony fournit-elle une partie de la solution?
Quelles recommandations ou idées pourriez-vous fournir? Existe-t-il des exemples d'applications symfony2 ou des applications open source disponibles qui ont mis en œuvre cette stratégie?
Ma première pensée est d'utiliser une tenant_id de colonnes de toutes les tables et se rapportent à l'objet de compte dans l'application. Je ne suis pas sûr cependant si ACL est censé prendre soin de ce que je veux faire, ou si votre toujours responsable de toutes les requêtes contre vos objets afin qu'ils ne retournent pas les données non autorisées.
si je n'utilisais pas la Doctrine, il pourrait être facile de dire juste ajouter Where tenant_id = @accountid
pour chaque requête, mais je ne suis pas sûr que ce soit la bonne approche ici.
Merci
6 réponses
nous faisons cela depuis un certain temps maintenant (bien que pas avec symfony et doctrine mais les problèmes restent les mêmes) - nous avons commencé avec une base de données énorme et mis en œuvre un "identificateur d'environnement" par ligne dans chacune des tables. De cette façon les migrations de schéma étaient faciles: tout le code était unifié ainsi un changement de schéma était un changement simple de code et de schéma.
cela conduit cependant à des problèmes de vitesse (grandes tables), d'agilité (déplacement/sauvegarde etc grands ensembles de données est beaucoup plus intensif bien sûr, il est plus facile de briser les environnements, car une seule défaillance détruira tous les ensembles de données du système...
nous sommes ensuite passés à plusieurs bases de données; chaque environnement possède son propre schéma. En utilisant les migrations fournies avec la Doctrine (1 dans notre cas) nous sommes en mesure de mettre à jour rapidement l'ensemble de l'application ou juste un seul environnement. De plus, la possibilité de déplacer des changements spécifiques entre les tentes permet une meilleure précision dans la vitesse et optimisation.
mon conseil serait: créer un noyau unique qui est étendu dans les différents locataires et garder la configuration locale de base de données personnalisée par tente. (dans une application.structure de type ini)
i.e.
/
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
Cela pourrait garder tout organisé. Nous allons même jusqu'à avoir la structure comeplete de noyau disponible par tente. Ainsi, il est possible d'outrepasser tous les aspects du "noyau" propre au locataire.
Nous le faisons avec l'une de nos principales solutions au travail, et c'est certainement possible. Nous utilisons les paquets de Symfony2 pour créer un paquet "base" qui est ensuite étendu par d'autres paquets par client.
cela dit, nous envisageons de nous éloigner de faire les choses de cette façon à l'avenir. La décision d'opter pour la location à plusieurs locataires n'était pas la bonne pour nous, car bon nombre de nos clients sont mal à l'aise à l'idée que leurs données figurent dans les mêmes tables de base de données que celles des autres clients. C'est complètement de côté des problèmes potentiels de la performance lente que les tables se développent.
nous avons aussi constaté que la Doctrine 2 comporte des problèmes assez graves à moins qu'elle ne soit bien maîtrisée. Bien que cela puisse être un effet secondaire du code mal structuré et de la logique de la base de données, je pense que c'est un peu un trou pour un ORM pour être en mesure d'arriver au point où il jette une erreur fatale parce qu'il est utilisé trop de mémoire - en particulier quand la seule raison pour laquelle il utilise autant de mémoire est parce qu'il est batching vers le haut des requêtes SQL pour qu'ils puissent être rendus "plus efficaces".
c'est purement mon opinion, bien sûr :) ce qui ne fonctionne pas pour nous peut bien fonctionner pour vous, mais je pense que vous feriez mieux de garder des bases de données séparées par client, même si elles sont toutes stockées sur le même serveur.
le meilleur construit différents avis dans un esprit différent. Veuillez être plus précis pour poser des questions. L'une des façons d'élaborer un système à locataires multiples consiste à mettre une clé primaire commune dans toutes les tables pour établir la relation. Le type et la nature de la clé primaire est fiable projet de sage.
pourquoi ne pas essayer différentes bases de données pour chaque client pour garder les données séparées et leur donner un point d'entrée unique à votre application. Ex:http://client1.project.net qui, avec le système d'acheminement, correspondra à la base de données client1.Le mauvais côté de cela: les changements de base de données plus complexes, parce que toutes les bases de données pour chaque client doit être mis à jour.
je pense que, pour gérer multi-locataire multi-base de données avec symfony 2/3.
Nous pouvons config auto_mapping: false
pour l'ORM doctrine.
fichier: 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
après cela, nous ne pouvons pas gérer la connexion de chaque locataire en outrepassant les informations de connexion dans request_listener comme article: http://mohdhallal.github.io/blog/2014/09/12/handling-multiple-entity-managers-in-doctrine-the-smart-way/ J'espère que, cette pratique peut aider quelqu'un qui travaille avec multi-locataire
Cordialement,
Vuong Nguyen
C'est quelque chose que j'ai essayé de comprendre aussi. Le mieux que j'ai pu trouver (pas encore dans une implémentation, mais en théorie) est ceci: donner à chaque locataire son propre login de base de données et utiliser des vues pour les empêcher de voir les données des autres.
fondamentalement, vous avez vos tables de base de données réelles, mais chaque table a une colonne qui stocke le nom de l'utilisateur de la base de données qui a fait la rangée. Les vues sont alors créées qui filtrent toujours par cette colonne donc chaque fois qu'un utilisateur se connecte à la base de données (via un outil d'administration ou même en se connectant via Symfony/Doctrine), elles ne sont que des enregistrements retournés directement associés à elles. Cela vous permet de garder les données "séparé", mais toujours dans une base de données. En tirant des données (disons pour une entité dans Symfony), vous tirez des données d'une vue filtrée par rapport à la base de données réelle table.
maintenant, cette solution n'est pas exactement Symfony/Doctrine amical. J'ai été en mesure d'obtenir un test très rapide et rudimentaire de cette exécution avant; Doctrine a été en mesure d'utiliser les vues de base de données juste très bien (il pourrait insérer, éditer, et supprimer des entrées d'une vue aucun problème). Cependant, quand vous faites des choses comme créer/mettre à jour le schéma, ce n'est pas amusant. Certes, Symfony / Doctrine semble assez extensible, donc je suis sûr qu'il y a un moyen de l'automatiser, mais ce genre de configuration n'est pas pris en charge hors de la boîte. Doctrine devrait être dit pour mettre à jour les tables, toujours ajouter la colonne pour tenir le nom d'utilisateur aux tables d'entité qu'il crée, mettre à jour les vues ainsi, etc. (Vous aurez également besoin d'un moyen de charger la config de base de données appropriée dans votre application Symfony, principalement les logins différents que le serveur et d'autres choses seraient les mêmes.) Mais, si cela peut être surmonté, votre application elle-même pourrait exécuter ces locataires multiples complètement "ignorant" le fait que les données d'autres personnes est assis dans la base de données.