I have three old applications (running on Symfony 2) where each one has been developed in separated git repositories and configured in their respective vhosts:
company.com
Company website.admin.company.com
Website administration.api.company.com
API company service.
Even though, they share the same database. So we're decided (the Company) unify all of them in one application with Symfony 4 structure & approach, mainly to remove a big quantity of duplicated data and to improve its maintenance.
Right now, I'm integrating all in one application/repository as was planned, but I'm starting to deal with some performance & structure issues:
- As I've just one entry point
index.php
I did two routes prefixes to be able to access forcompany.com/admin/
andcompany.com/api/
sub app, so all routes are loaded each time :( - All bundles and configuration is loaded and processed needlessly for each request. For example: when I access the API path the
SonataAdminBundle
is loaded too :( - The cache clear command takes a long time to complete.
- The tests are breaking down and now takes a long time to complete too.
I'd like to keep the early vhost and load just the needed bundles and configuration per domains:
company.com
Loads bundles, routes and configuration for a company website only (SwiftmailerBundle
, ...)admin.company.com
Loads bundles, routes and configuration only for website administration (SecurityBundle
,SonataAdminBundle
, ...)api.company.com
Loads just the bundles, routes and configuration to provide a fast API company service (SecurityBundle
,FOSRestBundle
,NelmioApiDocBundle
, ...)
This is what I'm doing so far:
// public/index.php
// ...
$request = Request::createFromGlobals();
$kernel = new Kernel(getenv('APP_ENV'), getenv('APP_DEBUG'));
// new method implemented in my src/kernel.php
$kernel->setHost($request->server->get('HTTP_HOST'));
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
I've check the current host prefix in Kernel::registerBundles()
method and I loaded the needed bundles only, but still I've problems with bin/console
file (it doesn't work as HTTP_HOST
variable is not defined for CLI) I'd like to clear the cache for each "sub-app" and so on.
I have been doing some research on this topic but so far I couldn't find anything helpful for my scenario (Symfony 4).
Is possible to have many applications under one project repository running independently (like individual apps) but sharing some configuration? What is the best approach to achieve it?
Thanks in advance.
Likely the multiple kernels approach could be a good option to solve this kind of project, but thinking now in Symfony 4 approach with environment variables, structure and kernel implementation, it could be improved.
Name-based Virtual Kernel
The term "Virtual Kernel" refers to the practice of running more than one application (such as
api.example.com
andadmin.example.com
) on a single project repository. Virtual kernels are "name-based", meaning that you have multiple kernel names running on each application. The fact that they are running on the same physical project repository is not apparent to the end user.In short, each kernel name corresponds to one application.
Application-based Configuration
First, you'll need replicate the structure of one application for
config
,src
,var
directories and leave the root structure for shared bundles and configuration. It should look like this:Next, making use of the
Kernel::$name
property you can stand out the application to run with dedicated project files (var/cache/<name>/<env>/*
):<name><Env>DebugProjectContainer*
<name><Env>DebugProjectContainerUrlGenerator*
<name><Env>DebugProjectContainerUrlMatcher*
This will be the key of the performance as each application has by definition its own DI container, routes and configuration files. Here is a complete sample of the
VirtualKernel
class that supports the previous structure:src/VirtualKernel.php
Now your
\VirtualKernel
class requires an extra argument (name
) that defines the application to load. In order for the autoloader to find your new\VirtualKernel
class, make sure add it tocomposer.json
autoload section:Then, run
composer dump-autoload
to dump the new autoload config.Keeping one entry point for all applications
Following the same filosofy of Symfony 4, whereas environment variables decides which development environment and debug mode should be used to run your application, you could add a new
APP_NAME
environment variable to set the application to execute:public/index.php
For now, you can play with it by using PHP's built-in Web server, prefixing the new application environment variable:
Executing commands per application
Add a new console option
--kernel
to be able to run commands from different applications:bin/console
Later, use this option to run any command different to default one (
site
).Or if you prefer, use environment variables:
Also you can configure the default
APP_NAME
environment variable in the.env
file.Running tests per application
The
tests
directory is pretty similar to thesrc
directory, just update thecomposer.json
to map each directorytests/<Name>/
with its PSR-4 namespace:Again, run
composer dump-autoload
to re-generate the autoload config.Here, you might need create a
<Name>WebTestCase
class per application in order to execute all tests together:test/Admin/AdminWebTestCase
Later, extends from
AdminWebTestCase
to testadmin.company.com
application (Do the same for another ones).Production and vhosts
Set the environment variable
APP_NAME
for each vhost config in your production server and development machine:Adding more applications to the project
With three simple steps you should be able to add new vKernel/applications to the current project:
config
,src
andtests
directories a new folder with the<name>
of the application and its content.config/<name>/
dir at least thebundles.php
file.composer.json
autoload/autoload-dev sections the new PSR-4 namespaces forsrc/<Name>/
andtests/<Name>
directories and update the autoload config file.Check the new application running
bin/console about -k=<name>
.Final directory structure:
Unlike multiple kernel files approach, this version reduces a lot of code duplication and files; just one kernel,
index.php
andconsole
for all applications, thanks to environment variables and virtual kernel class.Example based-on Symfony 4 skeleton: https://github.com/yceruto/symfony-skeleton-vkernel Inspired in https://symfony.com/doc/current/configuration/multiple_kernels.html