The problem: You have multiple services you want to build/ci/deploy. You want them all to have consistent dependencies between them all (one of the big reasons to utilize a monorepo).
It’s not difficult, you just have to think of your project as being a single segmented project. You don’t even have to utilize any extra funky composer plugins which might do weird things like making symlinks to bin commands.
Instead of having a composer.json file in each service, move that up to the level where all of your project directories live and generate a composer.json there.
Now, this part will take a little time… you will need to merge all of the requirements for all of your projects into the new composer.json.
Also in that composer.json, you will need to add autoload definitions for all of your projects… for example:
{
...
"autoload": {
"psr-4": {
"SomeCo\\Billing\\": "billing/src/",
"SomeCo\\Accounts\\": "accounts/src/",
"SomeCo\\Auth\\": "auth/src/",
"SomeCo\\DB\\": "libraries/DB/src/",
"SomeCo\\Config\\": "libraries/Config/src/"
}
}
...
}
Now, when you do your build step, you will run composer install from the new root project. This will build a vendor directory as a sibling to your project directories. To build the individual project, you will copy the vendor directory and the single project to your vm/docker image/whatever. If you have shared libraries, like the above example, you would also copy the libraries directory.
Of course, in doing this, you will have all dependencies from all of your projects available to the single project, but does it matter? It’s not like this is a 5GB node_modules directory (I exaggerate a little).
Now, the next step is likely going to take the most time. In your project, you must re-define where you fetch your autoload.php file from. In many cases, you can find/replace /autoload.php to /../autoload.php, but you should definitely be careful when doing this depending on what your build expects.
The other big change is if you run any commands from the vendor directory, you will have to redefine where they run from {project}/vendor/bin/xyz to vendor/bin/xyz.
Is that it? Well, yeah. The generated autoloader takes care of everything else, and there’s no funky hackery. Of course, yes the autoloader will have references to your other projects’ namespaces, but your individual projects should not be calling other projects’ namespaces.
Another bonus from using this method in development is you can relatively easily run all of your projects from a single set of containers instead of a set for each project.
Don’t forget to commit your composer.lock file! This ensures dependency consistency between development, CI/testing, and production!
Updating dependencies is relatively simple now.
composer outdatedcomposer update {whatever you decide to}- commit new lock file
You may want to document where certain dependencies are used, so maybe create a DEPENDENCIES.md when you start copying dependencies into the new composer.json.