Manage Composer dependencies in your monorepository project

This article was published more than 6 months ago, this means the content may be out of date or no longer relevant.

Having a monorepository project means to get all applications and components at the same place. This post isn't about the avantages or drawbacks of this project management strategy, a lot of developer blogged about the advantages or drawbacks. This post is about how to manage your Composer dependencies in the specific case of monorepository based project.

First, have a look of a project folder structure :

applications/
    api/
        composer.json
    backend/
        composer.json
    frontend/
        composer.json
    worker/
        composer.json
component/
    package1/
        composer.json
    package2/
        composer.json

The challenge is to define how the differents applications can easily use the 2 components package1 and package2. Maybe your first idea is to configure Composer autoloading to reference all components which are in the repository. This looks like :

{
  "autoload": {
    "psr-4": {
      "Vendor\\Package1\\": "../../component/package1/src",
      "Vendor\\Package2\\": "../../component/package2/src"
    }
  }
}

This method works, but it's not the best solution because the composer.json file of different components is not used. And this is the file which is used to define components requirement and configuration. Furthermore, there is a duplication of autoloading configuration: in the components and in each application will used its.

If you read the Composer documentation, there is a concept of "repositories". A repository is a package source. It's a list of packages/versions. Composer will look in all your repositories to find the packages your project requires.

It's possible to add new repository sources like github, gitlab or vcs and even more. The one which is very interestant in our case is path. This will allow to refer to a local repository of the computer.

We're going to update our composer.json :

{
  "repositories": [
    {
      "type": "path",
      "url": "../../component/package1"
    },
    {
      "type": "path",
      "url": "../../component/package2"
    }
  ],
  "require": {
      "vendor/package1": "*",
      "vendor/package2": "*"
  }
}

Now, the Composer configuration refers to our local repository, we don't need to override the component configuration, whereas Composer will load it as any other dependency.

While I made some search about the best way to manage Composer dependencies in a monorepository, I discovered an experimental project, which is an Composer plugin dedicated to manage this project structure. I didn't test it, but it could be interesting. The project author have blogged about the tool.