Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to handle dependencies between components of a PHP framework using Git and Composer

Background

I am developing a framework in PHP. I started by making each component separately so that it can be used independently from the framework.

After creating four libraries A, B, C and D:

  • A has no dependencies

  • B and C require A

  • D requires A, B and C

Now I have some issues when releasing a new version of one library, I may have to change dependencies of others and have to release new versions for them. For example: new version of A means new version of B, C and D.

I looked how other frameworks like Symfony and Laravel solved this issue. I learned that they are using the subtree feature of Git and the replace feature of Composer. It works as follows:

  • Each component is in a read-only repository with his own composer.json

  • Each component can require other components but doesn't replace them.

  • The framework repository uses subtrees to include all components. So no need to require them using composer. But it should require all their dependencies (since this is no longer handled by Compser).

  • The framework replaces all its components.

I have also noticed that

  • A component repository contains only the source code (no unit tests !)

  • Laravel made the component Contracts just to store all the interafces of all component and each component is requiring that.

Questions

  1. Is my explanation of how Laravel and Symfony solved the issue correct ?

  2. Do I really have to remove tests from the components repositories and put them in the framework one ?

  3. If yes, how can someone who wants just to use a single component be sure it passes the tests regardless of the whole framework being passing the wole tests ?

  4. Do I have to make sure that all components dependencies are compatible and require them manually in the framework composer.json ?

  5. What is the point of having a component for Interfaces ? This could not be used standalone anyway !

  6. Is there a better way to solve this problem ?

P.S: here are links to A, B, C and D

like image 722
webNeat Avatar asked Feb 03 '17 11:02

webNeat


People also ask

What is use of composer in PHP?

Composer allows developers to specify project dependencies in a composer. json file and then Composer automatically handles the rest. Composer makes it easier to keep vendor libraries out of your repo, meaning that only application code goes in the git repository.

What is composer json PHP?

composer. json is a JSON file placed in the root folder of PHP project. Its purpose is to specify a common project properties, meta data and dependencies, and it is a part of vast array of existing projects. In most ways, it is a counterpart to . NET project file.

What are PHP dependencies?

Dependencies are PHP libraries, frameworks, and components that you can use in your web development projects. They help to make coding easier and more efficient, and most projects will rely on a number of them.


1 Answers

Now I have some issues when releasing a new version of one library, I may have to change dependencies of others and have to release new versions for them. For example: new version of A means new version of B, C and D.

  • You have a multi-repo approach.
  • Edit of A => new version of A => version bump needed in B, C and D.

I think the most important thing is to get away from using dev-master and versionize your components properly, once they are stablized and ready to be out of dev-phase. Then you might use Composers range operators (caret ^ & tilde ~) to automatically update to the latest released version in a certain major.minor version range. This helps a great deal and takes the tedious manual version updating work out of your hands.

  1. Is my explanation of how Laravel and Symfony solved the issue correct ?
  • It's not correct. The underlying development concept, publication and consumption of packages work differently, then to what you described.
  • They use a monolithic repo development style. It's a single repository, which contains code for group of packages. The opposite of the mono-repo is many-repo approach. The alternative are git submodules.
  • All modules/bundles of a framework and the framework core/kernel are in one repository! For Laravel it's https://github.com/laravel/framework/tree/5.4/src/Illuminate
  • Each module/bundle folder contains a composer.json and the framework itself contains a composer.json
  • This allows to "split out" the module folders into standalone read-only repositories. Using a custom git helper, e.g. git subsplit publish like Laravel uses https://github.com/laravel/framework/blob/636020a96a082b80fa87eed07d45c74fa7a4ba70/build/illuminate-split-full.sh or splitsh https://github.com/splitsh/lite, like Symfony uses
  • The development happens in the main repo.
  • Finally, from the user/consumer perspective (in the composer.json of your CMS/app whatever), you simply require a module/bundle from the "standalone read-only repository" source. This is many-repo, because your app depends on many repositories.

When you update a dependency using Composer, then Composer replaces your packages with a newer version.

  1. Do I really have to remove tests from the components repositories and put them in the framework one ?

No. You could also leave the tests in the /moduleA/tests folder and adjust your unit test collector.

  1. If yes, how can someone who wants just to use a single component be sure it passes the tests regardless of the whole framework being passing the wole tests ?

Two things. The subject under test is:

  • (a) the component, which is ideally independently testable and
  • (b) the framework, which consumes many components and tests functionalities, which rely on functions from multiple components (e.g. a core/kernel). You could also split a kernel out, once it stabilizes and is testable independently. (e.g. your component D)
  1. Do I have to make sure that all components dependencies are compatible and require them manually in the framework composer.json ?

The monorepo developer perspective: The developer/maintainer of a framework can only release a new version, when all unit-tests of all components and all unit-tests of the framework itself pass, right? Then he can start a subtree split and automatically versionize the new components.

The application developer perspective: Yes. As the user of components of a monorep you are simply consuming standaloen dependencies (from the read-only repos). That means you have to maintain the versions of the components you require in your composer.json manually or automatically.

  1. What is the point of having a component for Interfaces ? This could not be used standalone anyway !

Good question!

  • Maybe the developers want to do things differently and "keep things sorted"

  • Or, they have a bad optimization idea on their minds:

One could argue that interfaces are only development contracts. When all components are written against interfaces you could simply pull the plug on them, after testing and before doing a production release. In other words: you could leave the interfaces repository away and run an interface removal, when you are releasing for production.

Leaving the interfaces repo away would lead to "interfaces X not found" fatal errors. Then you run an "optimizer pass" over the rest of the classes and remove all "implements interfaceX" strings. Less files to include. Less code to parse. Less IO. And now i will probably be killed in the comment section by suggesting this :) And no, Laravel or Symfony are not doing this.

  1. Is there a better way to solve this problem?

I'd suggest to do the following: for a project with <5 components, use multi-repo. If >5 components, go monorepo.

In general there are not so many options to solve this:

  1. git submodules
  2. mono-repo
  3. multi-repo

Each of the approaches has pro's and con's.:

  • Updating git submodules a.k.a. git version bumping and submodule updating leads to git madness, because it'll be constantly broken. and git madness leads to the dark side. :)
  • Mono-repo is easy to maintain and easy to publish. It gives you easy maintainace for the developer and multi-repo for the consumer. You can replace/rename/refactor across all modules/components at once.
  • Many-repo is hard to maintain, when you have a large number of components.

See also:

  • https://www.tomasvotruba.cz/blog/2017/01/31/how-monolithic-repository-in-open-source-saved-my-laziness/
  • DrupalCon New Orleans 2016: The Symfony Monolith Repository
like image 107
Jens A. Koch Avatar answered Oct 04 '22 01:10

Jens A. Koch