Skip to content

Instantly share code, notes, and snippets.

@diaspar
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save diaspar/4b6caff8f684f3ee8671 to your computer and use it in GitHub Desktop.
Save diaspar/4b6caff8f684f3ee8671 to your computer and use it in GitHub Desktop.
Symfony2 - Multi tenant application

I have this complex problem to solve.

We have a symfony2 project with a typical src/name/bundleName with all the code. We have controllers, entities, domain, forms and resources as views and css/js code. We are using Doctrine to access the storage. This project works only for the USA branch of the company. (A)

Our client now is asking to use the same project, for its business in Europe (B). After some analysis, These are the requirements we have discovered:

  • One codebase and One repo.
  • The data, even users, is not shared between the A and B. There is no middle instance that can see both sets of data.
  • We are using mysql and ONE database for now. No postgre schemas or multiple independent databases.
  • To avoid code duplication, we have to use the same codebase.
  • We want to avoid conditionals in the code and views to recognize A and B as much as possible.
  • We can use 2 subdomains to recognize the systems. Users from A will go to a.company.com and users from B will go to b.company.com
  • 70% of the project is common (CORE) functionality and UI for A and B.
  • A and B should have a way to override the CORE. Controller methods, services, validations, entities, views, css/js
  • A and B can create new functionality that does not exist on the CORE and routes to access it.
  • We may have a C bundle for asia or a D bundle for africa. It's not a requirement but, it may happen in the future.

The best idea I have so far is:

  • The original bundle becomes the CORE.
  • We create 2 additional bundles that inherit from CORE and according to Symfony specs, they can override.
  • Those bundles could also create new functionality.
  • We register the bundles at appKernel level. We can have 2 different subdomains + vhost. Each vhost would have an env variable that symfony understands out of the box. We use that var for the conditional registration of the bundles. We could even use https://github.com/vlucas/phpdotenv and avoid the vhost.
  • We understand we can only register one at a time because that's a requirement from symfony.
  • A and B would have in their parameters a tenant identifier. Each table on the core will have now a tenant field, and we will globally modify DOCTRINE (walkers? filters?) to query all data per tenant. We will make sure DQL, query builder and things like find() work as expected.
  • We are not sure symfony2 bundle override will work for our custom classes, but theorically we can always extend the core custom classes and create new methods inside A and B.

So, we would like to know your ideas and ways to make this more robust.

thanks!

@matthiasnoback
Copy link

Thinking about this some more, you should really install a project for each tenant. Basically: the service container will be compiled only once for each environment, meaning you can't just switch bundles based on an env variable. That would cause the service container to regenerate all the time and worse, cause strange things to happen.
You could have two different sets of bundles in one installed project, but you would need to define separate environments for them, like tenant1_prod, tenant1_dev, tenant2_prod, tenant2_dev. Or else they would keep overwriting each other's service container (like I explained above).

@matthiasnoback
Copy link

A proper way for having multiple tenants would be to have a runtime configuration system, where based on who is authenticated, some things appear/are available and others are not. That would really allow you to have the same codebase. Then you can't use inheritance for bundles, but instead you then have to use composition (i.e. including templates from a "parent" bundle, etc.).

@diaspar
Copy link
Author

diaspar commented Oct 11, 2014

@matthiasnoback Thanks a lot for your comments! . You are indeed right, and the service container may produce unexpected results if I use one production environment. Having multiple environments may be cumbersome in our specific case.

So yes, we are opting for 2 different installations. We would keep one codebase, but at deployment time, we will define which child bundle is loaded.

One thing I don't fully understand is why we should decide what's available based on the auth user. In our case the tenants are companies, and the companies have totally isolated users. The other thing is not clear to me is why we shouldn't use bundle inheritance and use composition. I really want to avoid as much as possible if/else around the code.

And last, My way of saying thanks, I just bought your book: Principles of PHP Package Design on leanpub. I already had One Year With Symfony and I like it a lot. I am eager to read the new one. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment