Monorepos are hot right now, and not for the wrong reasons. This blog will try to demystify the trend of grouping all your applications and libraries into one single repository and see why using a Monorepo
in your next project is worth consideration.
To illustrate the use of Monorepos, we will use the example of a restaurant application.
Let’s imagine you are the owner of a restaurant called Happy Restaurant
, and you want to have an application that handles the following:
-
Customers can order from the Menu and check the status of the order.
-
Chefs can receive and update the preparation order status.
-
Carriers can receive and update the delivery order status.
-
The administrator can edit the Menu.
Monorepos in a Nutshell
Monorepo is a single version control repository such as git that contains multiple and distinct projects that can be deployed independently.
Hearing this for the first time sounds unusual; you may think something like: you can’t group all your projects in one git repo and expect everything to work.
You are right! You can’t, not without the proper tooling in place and a well-defined relationship between your projects.
We will take a quick look at Monorepo
tools and best practices in this post
Differences Between Monorepos, Polyrepos, and Monolith
Polyrepos
Polyrepos or Multirepos
are the opposite of Monorepos. Multiple distinct projects in different “git” repositories is considered a Polyrepo.
Contrarily, in a Monorepo, we have all our applications in the same “git” repository, and they can be deployed separately.
Using a Monorepo
in our Happy Restaurant
project means having four separate frontend applications and one backend application, broken down into the following structure:
-
Admin
-
Carrier
-
Order
-
Prepare-order
-
API
One might wonder, why not group all these applications into one project and save the trouble?
You CAN do that, and by doing so will be creating a Monolith
application. And this exact development strategy is a common misconception about a Monorepo
application.
Monolith
With a Monolith
application, you can use only one technology, platform, and deploy artifacts.
The four frontend and backend applications in our Happy Restaurant
project will have the same framework and language. And they need to be deployed to the same platform; web, for example.
On the other hand, with a Monorepo
, we can have applications with different technologies and platforms that can be deployed independently. For instance, our admin
application could be an Angular application deployed on the web, and our order
application could be an Ionic application deployed to Android and ios stores.
Benefits of Using a Monorepo
1. Code Sharing
One of Monorepo’s strengths is sharing code between applications and libraries. For example, suppose the design team decides to have a standardized primary button in all our applications. In that case, you will need to create a library that will encapsulate all the UI and logic for your Button, then import it into your applications.
With this in place, it’s also simpler to refactor your shared-btn
and update all your applications in one single commit.
One thing to note is that you do not need to publish the shared-btn
library to a registry; you can consume your library internally between your applications and other libraries.
Using TypeScript aliases and Angular, you will be importing your library as follows:
// login.module of Order application
import { SharedBtn } from "@shared/button";
@NgModule({
import:[SharedBtn],
....
})
Class LoginModule{}
Then use your shared-btn
in your template:
// login.component.html
<form>
<input type="email">
<input type="text">
<shared-btn></shared-btn>
</form>
2. Consistency in Your Applications
Switching to Monorepos significantly impacts maintaining a consistent code style and linting rules across all your applications and libraries.
Let’s say your team decided to use single quotes instead of double quotes.
With a Multirepo
structure, you will update all your repositories with the new rule. On the other hand, with a Monorepo structure, you will be updating this rule only once, and all your applications and libraries will benefit from the new update.
3. Visibility
With a Monorepo
structure, you can have up-to-date visibility on all your applications and libraries.
By visibility, we mean you can see the available applications and libraries in one place at any given time of your project lifecycle without jumping between repositories.
And suppose you are using a tool like Nx for managing your Monorepo. In that case, you have access to an excellent command that generates a dependency graph of all your applications and libraries.
Run:
$ npx nx dep-graph
Result:
There is no need for you to keep documentation of relationships between your applications and libraries.
In addition, the onboarding of new hires is less time-consuming; they can quickly figure out the relationships and get familiar with the project.
4. Collaboration
Working with a Monorepo
simplifies collaboration between teams inside the project.
For instance, it’s easier to read others’ code for inspiration on how to implement a feature or fix a bug.
You can also work on PR with backend and frontend, then merge your changes into a single commit.
Coupling frontend and backend in one commit might sound scary because, if you ever have a backend issue, for example, and you want to roll back to the stable version, the frontend will roll back too (because we have a single commit strategy).
To fix the backend issues and keep the latest stable frontend version, you will need another git branching work and a redeploy.
So if you prefer shipping code faster using single commits and you can handle the coupling issues, Monorepo
is your friend.
Challenges
-
Read rights: If your company does not want all developers to have read access to all source code, unfortunately, Monorepos will not help you in this context. (For now, at least).
Because the problem here is on the versioning control side, If you are using git, you can’t clone only one section of your source code.
To fix the read rights to everyone issue, you can, for example, extract the confidential part of your project into another repository and then try to publish to some registry the shared source code between the extracted repository and yourMonorepo
.
Using our example, we can do that if we have some backend secrets and want to keep them in a separate repository. We will need to version and publish the shared models, interfaces, DTOs… to a registry, then consume them in our backend. -
Build time: For enormous applications using
Monorepo
(more than 100 applications and libraries), you will notice slowness in build time and dependency installation. The slower build time is because, in yourMonorepo
, you will be architecting your project in a way that allows you to share code between your libraries and applications, which may sometimes result in building unnecessary applications and libraries.
However, you can solve this slowness with a tooling system that tries to build only the affected applications and libraries.
For example, when usingNx
, you will be running the following command:$ nx affected:build --base= origin/main --head=$PR_BRANCH_NAME # where PR_BRANCH_NAME is defined by your CI system
-
Learning curve: You will need to learn more about the tooling available and how to work with it. Sometimes, you will also need to know more about new architecture patterns like DDD or Micro frontends. Here is a good starting point to learn about Micro Frontends and Module Federation.
Tools to Help Manage Monorepos:
To manage your Monorepo
, you will need the proper tools for your use case. The available ones are :
-
Bazel (by Google): “A fast, scalable, multi-language and extensible build system.”
-
Gradle (by Gradle, Inc): “A fast, flexible polyglot build system designed for multi-project builds.”
-
Lage (by Microsoft): “Task runner in JavaScript mono repo.”
-
Lerna (by Nrwl): “A tool for managing JavaScript projects with multiple packages.”
-
Nx (by Nrwl): “Next generation build system with first-class
Monorepo
support and powerful integrations.” -
Pants (by Pants Build): “A fast, scalable, user-friendly build system for codebases of all sizes.”
-
Rush (by Microsoft): “Geared for large mono repo with lots of teams and projects. Part of the Rush Stack family of projects.”
-
Turborepo (by Vercel): “The high-performance build system for JavaScript & TypeScript codebases.”
For a more detailed benchmark of each tool’s features, check this awesome one initiated by the NX team.
Conclusion
Using a Monorepo
can be an excellent solution for projects with multiple technologies, production platforms, and teams. Monorepos
are also helpful for everyone who wants to enhance collaboration.
However, it’s a technology that should be consumed with caution and not blindly adopted.
Like any other software engineering topic, there is no one-size-fits-all. Your job as an Angular developer is to know each technology’s pros and cons and ensure it fits within your project constraints.
If you need help figuring out what those constraints are or how to think about them, our team of Angular experts can help. We’ve worked with projects of all sizes and can help yours succeed, too. Schedule your free consultation!
Previous Post
Next Post