This post is my opinion on the subject.Mono-Repo. After a short introductionMono-Reposand a comparison withMultirepos, set input toolsMono-Repos.
I don't want to evaluate in detail which type of repository is best under which circumstances. However, the purpose of this article is approximateMono-Reposand howdie Tone,npm, mifio(workrooms) It may help. It also makes sense to use these tools in combination. Particularlydie Tonemithread workspacesthey can peacefully coexist in a project. As? We'll find out in a minute.
tools likedie Tonemithread workspaceswere a deal breaker with the result that you keep your code base in a single repository (akaMono-Repo) gained traction for about a year or two. Many articles have been written or lectures given at conferences on this subject.
In short, a phone callMono-Repois a repository (Git) that hosts various projects. These projects are called workspaces or packages. In contrast, the use of multiple repositories is mentioned, with each repository hosting only one projectMultirepogetting closer. Of course, a combination of both approaches is also possible. At my current job, we build multiple teams, with each team having their own repositories. There are teams that pursue aMultirepoapproach and there are teams who believe in youMultirepomaximum. Additionally, there are teams that use both approaches because the technology that is part of the repository is also a factor in decision making (e.g. each Java microservice is part of its own Git repository).
Learn more about the differences and the pros and cons ofMono-ReposmiMultireposI recommend the article by Markus OberlehnerMonocamp in nature.
AMono-Repohosts one or more projects or packages. These packages are "mini-repos" that can be versioned, compiled, and released independently. Therefore, each package contains its ownPackage.jsonFile due to the fact that each package is a complete project in itself. Packages can have dependency relationships with each other. These dependencies are managed using symbolic links.
As discussed below,die Tonemithread workspacesIt gives us the ability to build libraries and apps in a single repository without being forced to publish to npm or other registries. The beauty of these technologies is that they can find package dependencies through analysisPackage.jsonFiles located in the root folder of each project. So these tools make it obsolete to create symlinks manually or to use "low-level".enlace npmImmediately.
This results in faster debug cycles for code tests when components are shared locally.die Tonemithread workspacestogether they improve the developer experience of managing multiple packages in oneMono-Repo.
link betweennpm,fio,thread workspaces, midie Tone
I want to shed some light on the chaosnpm,fio,thread workspaces, midie Toneare involved in the issueMono-Repos. Look at the "Assembly Diagram" below.
Describe three key players and how they correlate. By the way, don't take the proportions too seriously. The purpose of the diagram is just to give you an idea of how things are connected.
npm(marked with 1) andfio(2) both are native package managers that share many features (3). For example, both use the concept ofPackage.jsonas a dependency management container introduced bynpmin the past. The most commonly used concepts and features are dependency management, publishing, or using lock files to "freeze" dependency versions. There are more resourcesnpmwhich are also used byfiohow to post to the npm registry.
One of the reasons for creatingfioFirst there was performance - it took a long time to install dependencies on large projectsnpm. Another aspect was the lack of features, such as a fancy concept for freezing versions, offline features or deterministic behavior in terms of dependency resolution. However, many of these shortcomingsnpmit disappeared over time and today both technologies are becoming more and more compatible.
Things that still belong exclusively to itnpm(1) ofio(2) Sohnpackage lock.jsonfiles orwire.padlockFiles or For us application developers, the different implementation of lock files does not matter. Virtual,npmmifioit even goes into how versioning is handled.
A great resource that is exclusivefioEsthread workspaces(4) has been addedfioA year ago. it expandsfioby nativesMono-RepoCapabilities. The next section covers moreMono-RepoCharacteristics.
Mono repo - what is native? What is userland?
Consider the following diagram that describes how the technologies are connected in the mono repo environment.
Marked in red are technologies that provide mono repo capabilities. They are all based onnpmÖfio. Apart from that, the latter does not offer any advanced functions for creating mono reposesenlace npmÖwire connection, or.
thread workspacesis the only agent that exposes mono repo resources natively.die Tonebeen there for a while and left before eveningthread workspacesexisted.die Toneprovides mono repo resources at the user's country levelnpmÖfioas dependency management tools.
die Toneuses semantic linking for this purpose. It also allows you to usethread workspacesand then leave the whole mono repo aspect solely to the natively implemented features ofthread workspaces. Next to,die Toneoffers sophisticated release and release management features to release projects independently. Short,die Toneoffers many features beyond mono repo management. On the other hand,thread workspacesThe sole purpose is to facilitate the mono repo workflow. So you don't have to choose a side. It is very useful to usedie Tonecomthread workspaces.
screwis a relatively new project based onthread workspaces. Inspired bydie Tone, your goal is to add more useful commands to this base. However, I have no experience with it as I have not been able to get itscrewand runs on my playground project. I also noticed that there have been relatively few engagements lately. Therefore, I will not deepen this article any further.
The purpose of this section is to give a quick overview of how the different tools can be configured in different variations. The screenshots can be understood as a kind of "cheat sheet". The focus is on the configuration part of the different approaches and how they differ.
I made a small repository to demonstrate the different variants. JustClone demo project repositoryand switch branches for the different variants. EITHERLÉAME.mdThe file describes how the specific variant is initialized and used (i.e. the dummy application is created and run). Another goal of this demo and project section is to provide a simple playing field to see the different variants in action from different perspectives: what configuration steps are required, what steps are required to create the sub-projects (i.e. packages) and to how does the dependency work? Management work or what is the impact on boot time.
1. Do it yourself
I'm skipping this section, but feel free to check the branch.1-do it yourself. You basically work with usenlace npmand you need to create semantic links and install all subprojects manually. I hope you can imagine how boring and impractical this scenario is for real projects.
2. learn with npm
If you need help automating these manual tasks from Approach 1,die ToneIt was presented. you need oneTon.jsonfile in the root folder. As a conventiondie ToneEUnpmStandard.
Basically, as you can see in the screenshot below, you have to edit two files to get themdie Tonework:Ton.jsonmiPackage.json. WithinTon.jsonyou have to specify wheredie ToneYou need to look for packages.
To initialize any sub-projects you need to runbeginning of learningby calling the following npm script:
p.sRun npm boot
Basically what this command does is go to the root folders of all the packages and runinstall npm. Look at all three packages and you will seedie Tonemade create npm anode_modulesfolder for each package.
3. Clay with threads
This is the same configuration as approach 2. The only difference is that you must specifyfioas a client with the "npmClient" property turned on.Ton.jsonArchive. Bootstrapping is also performed bydie Tone.
How is it different from approach 1? Practically nothing. It's mainly a matter of taste, because the only difference is whetherdie ToneUsednpmÖfioas a dependency manager. The answer to the question of which one to choose boils down to the following questions:
- Which syntax do I prefer?Run npm <command>vs _hilo <command>
- Should I stick to the quasi-standard or like Facebook's efforts?
- Do I really care about the start time? If so, take a look at the next chapter, which includes some performance benchmarks.
4. Thread Workspaces
For this approach you don't needdie Tone.thread workspacescome with built-inMono-RepoCapabilities. Carrythread workspacesyou needfioVersion 1.0 or higher. As you can see in the screenshot below, you don't need a dedicated configuration file. EITHERPackage.jsonThe file in the root folder must be private and a"Working area"property sayingfiowhere you put the worksets (or workspaces infioNetwork).
To initialize the project with all its workspaces, just usefioout ofthread workspacesprovides this function natively:
p.sfioTo install
the cut:
p.sfio
This combines the two steps of approach 1 and 2: installing the dependencies from the root folder and initializing all package dependencies.
A big difference to approaches 1 and 2 is thisthread workspacesjust create onenode_modulesFile. All dependencies are elevated to the root folder. Note: This behavior is also possible withdie Tone(whichthread workspaces) Use of- the womanBandera.
5. wired Lerna workstations
Configuredie Tonecomthread workspacesThey must have the same configuration in rootPackage.jsonas described in approach 4. However, you must provide the following: aTon.jsonFile also in the root folder. So you have to countdie Toneusethread workspaces. Unfortunately, you have to specify the location of the sub-projects redundantlyTon.json. To initialize the project, nobeginning of learningit is necessary, you just have to use itDrahtinstallationas described in approach 4. It isMakes no sensegetting togetherbeginning of learningsince he only callsDrahtinstallationI when.
With this attitudedie Tonefully dedicated to the initialization and dependency workflowthread workspaces. So you need to configure more to get the same as above approach. So why would you use this method instead of approach 4? Well, think about it: usedie Tonemithread workspacesat the same time it makes perfect sense. They peacefully coexist together in oneMono-RepoProject.
In such a scenario:
- You use exclusivelythread workspacesfor himMono-RepoWorkflow.
- you usedie ToneHelper commands to speed up handling of multiple packages, e.g. B. Selective execution ofnpmTest scripts.
- you usedie Toneto publish packagesdie ToneIt offers sophisticated functionality with its share and publish commands.
The last section provides a quick understanding of the configurationMono-Reposwith different configurations. The focus of this section is more on the properties ofdie Tonemithread workspaces.
thread workspaces
updated,thread workspacesis the only technology that comes with native features forMono-Repos. In contrast todie Tone, you don't need to do a separate step to initialize the package dependencies.DrahtinstallationIt works by installing the dependencies from the root folder and then for each package.
In contrast todie Tone,thread workspacesIt does not include any additional features other than dependency management for multi-project setups. As its base isfio, you have everythingfioIn the hands.
Usethread workspaces, Facebook introduced some additional commands that only make sense in contextMono-Repos.
The following command displays the workspace dependency structure of your current project:
p.sInformation about the thread workspace
You can use the receipt below to do itfioCommand in the selected workspace (i.e. package):
p.sThread workspace <package name> <Domain>
As an example using the following commandreactis added to the package/workspace with the name "awesome package" as a dev dependency (instead of–Entwyou can also use-D):
p.sThread Workspace Pack awesome add responsive--dev
The following is an example of removing a dependency on a specific package:
p.sthread workspace web project remove some packages--save on computer
If you want to add a common dependency to all packages, go to the project root folder and use the command-C(Ö–Ignore workspace-root-check) Bandera:
p.sCables add a package-C
Otherwise you will get an error message forfio.
I add one of my own packages (awesome-components) as a dependency to another package (awesome-app) with the following command. I found that adding local packages otherwise has to be done by specifying a version numberfiotries to find the dependency in the registry.
p.sAdd thread workspace @doppelmutzi/awesome-app @doppelmutzi/awesome-components@0.1.0-D
Use the workspace feature,fiodoes not add dependenciesnode_modulesDirectories in one of your packages - 'root level only, i.e.fioraises all dependencies to the root level.fiouses symbolic links to refer to different packages. Therefore,fiocontains dependencies only once in the project.
you have to usethread workspaces'my wifeAbility to use incompatible third-party dependencies that are being worked onMono-RepoThe atmosphere. You must specify this in the root of the project.Package.jsonas you can see in the example below.
// Package.json{ ... "Working area": { "package": ["Package/*"], "no lift": [ "**/react-native" ] } ...}
For more information, seeDemo-ProjectvonConnectDotz.
die Tone
The same asthread workspaces,die Toneadd to Mono-RepResources for a frontend project. However, as described abovedie Toneit works in "user territory" and cannot add such features natively.
if you hiredie Toneusethread workspacesSodie Tonehands over all dependency managementthread workspaces. if you hiredie TonecomnpmÖfioSodie Toneoffers theMono-RepoResources alone by using symbolic links. In this context, you should usebeginning of learningto initialize the dependencies of all packages.
John Tucker has written an excellent article on how to use itdie TonevonCommands to initialize projects and manage dependencies.
To installreactAs a dependency of all packages you can use the following command:
p.sthe clays add react
If you want to installreactAs a dependency only for a specific package, run the following command:
p.sthe clays add react--AreaMy package
if you installed itreactfor each package, but I want to upgrade/downgrade to a specific version only for a specific package, so you can do it like this:
p.slerna agregar reacciar@16.0.0--AreaMy package
die Tonecomes with onepair of flags. They represent optionsdie Tones that need to be filtered.
Consider the following npm script named "test". The two shell commands below show how to run the test using the command only for specific packages-AreaBanner together with balloons.die Tonetry to walkWire testfor each matching pack.
// Package.json{ ... "hyphens": { "prove": "lerna spin test exec" } ...}
p.sfioprove --Area@services-my-company/*
p.sfioprove --Area@my-company/web-*
In accordance withDocumentation,die ToneIt also provides common height constraints for the root folder, just likethread workspaces' Default behavior. So you have to use those- the womanBandera.
p.sthe clays add react-D -- the woman
and usedie Tone, is a question to choosenpmÖfio. As you can see in the "cheat sheets" in the last section, you can easily switch between the different package managers at will.
Commands and advanced front-end workflow features
even if you choosethread workspacesfor dependency management it's a good idea to use itdie ToneAlso. The reason isdie Toneoffersutility commandsto streamline management of multiple packages. For example with onedie ToneIn the command, you can iterate through all packages or specific packages and perform a series of operations (such as linting, testing, and building) on each package. with that he greetsthread workspaceswhich takes over the process of dependency management.
Usedie ToneTesting or removing lints from within the root folder is faster than manually invoking all operations from each package folder. John Tucker's blog post is abouttest withdie ToneVery detailed.
Version control and release are major development issues wheredie Toneit also shines.die Toneallows you to use two modes of version control:
-
fixed/locked mode: The version of each package can be managed at a single point (inTon.jsonArchive). If a package has been updated since its last release, it will be updated to the new version. Consequently, a major change to one package results in all packages having a new major version.
-
Standalone-Modus: Package versions can be incremented independently of each other. So the key "version" in itTon.jsonshould be set to "Standalone". This approach offers much more flexibility and is particularly useful for designs with loosely coupled components.
You can publish packages that have changed since the last release:
p.slearn publicly
In standalone mode there are several options to affect the release push with the publish command. In addition to using aAlwayskeyword, you can also use one of the following version upgrade flags:de-goÖof the package.
The following command is written to an npm log while usingconventional confirmation pattern.
p.slearn publicly--conventional obligations --sim
The above command also generates change log files. In accordance withTone Document, there are different changelog presets likesquareÖBit Bucket. By the way theyes flagignores all confirmation requests.
WithinTon.json, you can globally define to use traditional commits without using flags:
// Ton.json..."Domain": { "Post": { "conventional obligations": TRUE, "sim": TRUE }}...
@jsilvaxExplainas the conventional is obligeddie ToneIt works outand how it can be appliedcommit.
Since version control and publishing are complex topics, the above section only shows a small part of them.die ToneOpportunities. I won't go into detail as it is beyond the scope of this article.
One of the main reasons people commitfioinstead ofnpmis the performance in terms of time to install dependencies. Originally,fiowas developed from itnpmIt took a long time to install dependencies (aside from thatnpmsome important functions were missing). Includednpmis available in version 6 and has made a lot of effort to close this gap.
because you can reachMono-RepoLet's take a look at how these different approaches work. In the rest of this section, I present the results of my performance experiment. i clonedThe babel project(circa October 2018) because it represents a realityMono-Repowith many packages (142 to be exact). Interestingly, the original configuration ofBabelUseddie Tonewith a configuration that specifiesfioifnpmClient(NOthread workspaces) and disablefioLock file generation.
For each approach (2 - 5) I did the following:
- I've changed the required settings to the appropriate approach (i.e. adjustedPackage.jsonmiTon.jsonIf required).
- I measured the elapsed time for installing dependencies and for a dedicated initialization step (if needed).
- I timed 3 different use cases. I carried out 3 measurements for each application.
The use cases (UC) mentioned are:
1) I cleared npm or threads cache and cleared everythingnode_modulesDelete folder and allpackage lock.jsonÖwire.padlockfiles.2) The cache exists, I delete everythingnode_modulesDelete folder and allpackage lock.jsonÖwire.padlockfiles.3) cache exists,package lock.jsonÖwire.padlockThere are files, I delete them allnode_modulespasta
To clear the cache I ran one of the following commands depending on what was usednpmClient:
p.snpm cache cleaning--fortaleza
Ö
p.sClear connection cache
As a helper to remove lock files andnode_modulesfolder I added a script to the babel root folder calledlimpieza.sh:
meet. -TypF-Name 'wire.lock' -Executive rm {}+find. -TypF-Name 'package lock.json' -Executive rm {}+find. -Name "modules_nodes" -TypD-Plum -Executive rm -rf '{}'+
Depending on the use case, I commented out the first two lines.
To measure the execution time of the dependency initialization and installation steps I usedStyle. The following command is an example of approach 2 (die Tonecomnpm) and UC 1 (empty cache, nonode_modulesfolders, without locking files as a requirement) to measure elapsed time:
p.snpm cache cleaning--fortaleza &&./limpieza.sh&&npm a | style&&npm start boot | style
Below you will find the various measures. I've taken these measurements over time and then played around with different onesIs,npm,fio, midie ToneVersions to find out if different versions have different impacts on performance.
ChangeIsminpmversions I have usednvm. The following example installs and uses firstv9vonIsand then installv5.7.1vonnpm.
p.snvmTo installv9p.snvm usa v9p.sasl y- Grammnpm@5.7.1
Enfoque 2 (learn with npm) – Node v10.12.0 / npm v6.4.1 / learn 2.11.0
KU | To install | Ohr | Generally |
---|---|---|---|
1 | 39.1680s | 64.7168s | 103,8848s |
1 | 40.8052s | 78.0730s | 118,8782s |
1 | 39.8729s | 64,0626s | 103,9355s |
2 | 23,9931s | 34.8695s | 58,8626s |
2 | 23,8788s | 38.7979s | 62,6767s |
2 | 25,4764s | 37.5166s | 62.993s |
3 | 16,7291s | 35.8081s | 52,5372s |
3 | 29.4270s | 72,3721s | 101,7991s |
3 | 39.4265s | 85,0043s | 124.4308s |
Note: To be honest I'm not sure why the offsets are so high for the last two entries. Maybe the utilization of my Macbook was too high?
Enfoque 2 (learn with npm) – Node v9.10.0 / npm v5.6.0 / learn 2.11.0
KU | To install | Ohr | Generally |
---|---|---|---|
1 | 38.1641s | 52,7642s | 90,9283s |
1 | 33,3413s | 57,4676s | 90,8089s |
1 | 32.3160s | 52,4869s | 84,8029s |
2 | 24,3268s | 41.6709s | 65.9977s |
2 | 26,4843s | 41,6038s | 68.0881s |
2 | 29.8368s | 43.3759s | 73.2127s |
3 | 18.2647s | 33,7095s | 51,9742s |
3 | 15,2864s | 33,4166s | 48.7030s |
3 | 15,9295s | 34,6834s | 50.6129s |
Focus 3 (Lerna Wired) – Knoten v10.12.0 / Fio 1.10.1 / Lerna 2.11.0
KU | To install | Ohr | Generally |
---|---|---|---|
1 | 36,5181s | 58,5693s | 95,0874s |
1 | 29,9026s | 53,8042s | 83,7068s |
1 | 30.8910s | 60,2566s | 91,1476s |
2 | 15,6954s | 34,9247s | 50.6201s |
2 | 24,4038s | 36.8669s | 61.2707s |
2 | 16.1917er | 36,4996s | 52,6913s |
3 | 9,2134s | 29.0799s | 38,2933s |
3 | 10.1278s | 27.1641s | 37,2919s |
3 | 10,2387s | 28.1842s | 38,4229s |
Focus 3 (Lerna Wired) – Knoten v9.10.0 / Fio 1.10.1 / Lerna 2.11.0
KU | To install | Ohr | Generally |
---|---|---|---|
1 | 52,3567s | 69,5431s | 121,8998s |
1 | 45.3363s | 56,1238s | 101.4601s |
1 | 40.0621s | 54,2408s | 94,3029s |
2 | 23.2312s | 40,1567s | 63,3879s |
2 | 22.7905s | 39,2331s | 62.0236s |
2 | 21,3754s | 37,9659s | 59,3413s |
3 | 13,4165 s | 28,6476s | 42,0641s |
3 | 13,2283s | 27,9781s | 41,2064s |
3 | 12,6465 s | 29.3560s | 42,0025s |
Focus 4 (cable workstations) - node v10.12.0 / cable 1.10.1
An "initialization" step is not required becauseDrahtinstallationit does it under the hood.
KU | To install | Ohr | Generally |
---|---|---|---|
1 | 34,9199s | 34,9199s | |
1 | 31,8336s | 31,8336s | |
1 | 32.6647s | 32.6647s | |
2 | 17,9583s | 17,9583s | |
2 | 17,7032s | 17,7032s | |
2 | 17.9703s | 17.9703s | |
3 | 12.6103s | 12.6103s | |
3 | 13.4137s | 13.4137s | |
3 | 12,8213s | 12,8213s |
Focus 4 (cable workstations) - Node v11.2.0 / Cable 1.10.1
KU | To install | Ohr | Generally |
---|---|---|---|
1 | 65.1631s | 65.1631s | |
1 | 69,0633s | 69,0633s | |
1 | 63.1915er | 63.1915er | |
2 | 25.6090s | 25.6090s | |
2 | 22.4050s | 22.4050s | |
2 | 24.7715s | 24.7715s | |
3 | 18.0540s | 18.0540s | |
3 | 18,8891s | 18,8891s | |
3 | 17.0438s | 17.0438s |
Approach 5 (lena with Yarn workspaces) – Node v11.6.0 (npm v6.5.0-next.0) / Yarn 1.12.3 / lerna 3.8.0
With this approach I am trying to find out if you are usingthread workspacesas part of adie ToneConfiguration makes a difference compared to approach 4. Because there is nonebeginning of learningrequired, the corresponding column is empty.
But as I expected there is no difference in approach 4 likedie ToneIt is not involved in the dependency initialization/installation process.
KU | To install | Ohr | Generally |
---|---|---|---|
1 | 60.4779 | 60.4779 | |
1 | 63,3936s | 63,3936s | |
1 | 58.1888er | 58.1888er | |
2 | 32,7976s | 32,7976s | |
2 | 30.8835s | 30.8835s | |
2 | 28.9111s | 28.9111s | |
3 | 16,4637s | 16,4637s | |
3 | 17,8068s | 17,8068s | |
3 | 16.3400s | 16.3400s |
Approach 6 (lerna + npm ci + audit) – Node v10.12.0 / npm v6.4.1 / lerna 3.4.3
In this approach I usedie Tonecomlike youwhat is an alternative toinstall npmin the context of continuous integration. Since version 3 ofdie Tone,like youis the default value for the install command. However, you can decide not to participate.
For this approachpackage lock.jsonThe files must exist.node_modulesThe folders must have been excluded, otherwise warnings are given in the terminal. Therefore UC 3 is not possible.
KU | To install | Ohr | Generally |
---|---|---|---|
1 | 7,9733s | 34,1282s | 42.1015s |
1 | 9,3572s | 35.0904s | 44.4476s |
1 | 8,9436s | 36,3684s | 45.31200s |
2 | 10,8888s | 49,3526s | 60,2414s |
2 | 10,9077s | 44,9243s | 55.8320s |
2 | 11,5785s | 43.6369s | 55,2154s |
Enfoque 6 (lerna + npm ci) – Nodo v9 / npm v5.7.1 / lerna 3.4.3
Use exactly thesenpmversion, thelike youThe command is available but without therevision function. I wanted to test this setup to see if there is any performance impact without checking the dependencies. Again, UC 3 is not possible in this scenario.
KU | To install | Ohr | Generally |
---|---|---|---|
1 | 9,0732s | 29,8326s | 38,9058s |
1 | 9,3738s | 30.0418s | 39,4156s |
1 | 8,8552s | 29.1426s | 37,9978s |
2 | 11,7469s | 39,9573s | 51,7042s |
2 | 13.3401s | 44,6026s | 57,9427s |
2 | 13.3603s | 39.9416s | 53.3019s |
Based on my measurements, I see no noticeable difference betweennpmmicable (workstations)in terms of performance. From the point of view of the resource, the two are not differentiated either. For me it is a matter of taste which package manager I use. In addition, they can be exchanged or used together at any time.
Currently I prefer to usethread workspacesifMono-RepoTechnology because I like its lifting power. On the other hand, this is also possible withdie Toneand your- the womanFlag. In my opinion,thread workspacescomdie Toneis a good combination: Configuredie ToneLeave dependency management onthread workspacesand use your utility commands.