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!
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).