Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(website-new): add monorepo information to the practice section #3351

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/website-new/docs/en/practice/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
"label": "Frameworks",
"collapsed": true
},
{
"type": "dir",
"name": "monorepos",
"label": "Monorepos",
"collapsed": true
},
{
"type": "dir",
"name": "scenario",
Expand Down
14 changes: 14 additions & 0 deletions apps/website-new/docs/en/practice/monorepos/_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"type": "file",
"name": "index",
"label": "Benefits of Module Federation in a Monorepo",
"collapsed": true
},
{
"type": "file",
"name": "nx-for-module-federation",
"label": "Using Nx for Module Federation",
"collapsed": true
}
]
99 changes: 99 additions & 0 deletions apps/website-new/docs/en/practice/monorepos/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Benefits of Module Federation in a Monorepo

Module Federation is a powerful tool that allows you to share code between applications. It enables you to build applications that are more modular, maintainable, and scalable. In this guide, we will explore the benefits of using Module Federation in a monorepo.

Polyrepos (multiple repositories) have long since been the de facto choice for developing micro-frontends, to combat organizational issues, however, they introduce more organizational challenges around code sharing, dependency management, versioning and team autonomy.

Enabling team autonomy via Micro Frontends inadvertently pushed teams towards a polyrepo structure. Each team would have their own repository and would be responsible for maintaining their own codebase. This approach led to a lack of collaboration and a lack of transparency among other challenges.
Coly010 marked this conversation as resolved.
Show resolved Hide resolved

You can get all the benefits of Module Federation without the hassle of managing multiple repositories when you choose a monorepo, including some additional benefits around code sharing and dependency management.
This guide will help you understand the benefits of using Module Federation in a monorepo and how they address organizational challenges.

## Flaws with the Polyrepo Approach
Copy link
Collaborator

@zackarychapple zackarychapple Dec 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe instead of flaws we call this "Potential drawbacks of a polyrepo approach" or "potential issues"


Total isolation can be very desirable for teams but it comes with its own set of caveats and issues, especially with Module Federation. It's easy to forget that when working with a Module Federation system, the end result that the user experiences is a single application.
As such, the total isolation and autonomy over tech decisions for polyrepos that comprise portions of the single application can actually lead to inconsistencies in the final experience for the user.

Let's take a look at some of the other challenges that arise with the polyrepo approach.

### Feedback Loops

The polyrepo approach can lead to a lack of transparency and feedback loops. This can be especially problematic when working with a Module Federation system.

When one team makes a change to their portion of the application, it can be difficult to understand how it will affect the other parts of the application. Notification of runtime issues and breaking changes cannot be found until after a deployment has occurred.

Even if there is a quality assurance process in place to ensure that changes are safe before reaching production, the feedback loop is slow and can be disruptive, forcing teams to either roll back changes or quickly fix issues that arise instead of adding new features.

### Shared Code Strategies

Sharing code between polyrepos is difficult. It tends to involve creating and maintaining a private package registry of some form. Shared code gets built and deployed to this registry and consumed by the other polyrepos. If a change is made to the shared code, it must be released to the registry.

This can be time-consuming and error-prone! It introduces the risk of blockers to feature work if any of the teams rely on the PR for the shared code to be released to the registry.

It also circles back to the feedback loops issue, as teams must wait for the registry to be updated before they can see the impact of their changes, if they ever see the impact at all because of the isolated teams. They could be inadvertently introducing breaking changes that affect other teams.

The teams consuming the shared code must also actively update the versions of the shared packages they are using to ensure compatibility across the module federated system, or risk breaking changes at runtime.

### Dependency Management

Polyrepos can also lead to dependency management issues. When teams are working in isolation in their own repository, they may have different dependencies or different versions of dependencies. This can lead to conflicts, versioning issues or worse, increased bundle sizes because of duplicated dependencies.

For example, if one team is using React 18 and another team decides they want to use React 19 so they update their dependency, then once the changes are deployed to production there is a high risk of incompatible code and runtime errors, especially if other portions of the application do not support React 19.

In a polyrepo setup, the update of React 19 does not immediately inform the team that by doing so they will be breaking the other teams. This can lead to a lot of frustration and rolling back of changes.

### Testing Strategies

When teams are working in isolation, they may not have the same testing strategies or tools. This can lead to inconsistent testing across the system, which can be a problem for the overall quality of the application.

It also adds difficulty to the QA process. Either they test each micro-frontend separately or they test all of them together.

Testing individual micro-frontends does not provide the same level of confidence in the overall quality of the application because it does not test the integration points nor any shared context between them.

It also means having to build some kind of test framework that can mock certain layers of the full application which introduces a maintenance tax that must be paid to ensure that the test framework stays up to date with the other portions of the application.

Testing the full application will provide a higher level of confidence overall, however, in a polyrepo setup it requires setting up configurable test environments that can deploy the latest versions of each micro-frontend.
This is time consuming, costs money on cloud resources and means that feedback on issues that arise during testing is delayed, reducing overall iteration speed.

## How does a Monorepo address these flaws?

A monorepo is a single repository that contains all the code for a project. It is a single source of truth for the entire project.
Let's take a closer look at each of the challenges above and how a monorepo can address them.

### Feedback Loops

In a monorepo, the feedback loop is simplified because all the code is in the same repository. This means that changes that are made by individual teams will present immediate feedback on how they impact the other teams and micro-frontends. This eliminates the need for manual releases and reduces the risk of breaking changes.

It informs the developer of the impact of their change at the time of making it, allowing them to make the necessary changes to ensure that the impact is minimal.

### Shared Code Strategies

With monorepo tooling such as [Nx](https://nx.dev), teams can share code without the need for a central package registry. This eliminates the need for teams to manually release changes to the registry and reduces the risk of conflicts and versioning issues.

It also greatly improves the speed of development as it cuts out the process of updating a separate repo of shared code, waiting for a new release to the central registry and then installing the new version in the consumers.

Any changes to shared code can be made in the same repository as the code that consumes it. This increases collaboration and transparency across teams while improving quality (changes will provide immediate feedback) and decreasing the risk of breaking changes.

Observability into what is using the shared code is also increased with help from dependency graphs and IDE support. This can help developers make informed decision about how impactful their changes will be.

### Dependency Management

In a monorepo single version bumps of major dependencies such as React or Angular are applied to all consumers. This ensures maximum compatibility across the system and reduces the risk of breaking changes.

It _does_ require teams to collaborate and align on when to perform these upgrades, but it results in more confidence and compatibility for the end user.

Teams can still choose to use their own versions of other minor dependencies such as `date-fns` or `lodash` if they choose to do so by defining those dependencies in their own `package.json`, or by aliasing them in the root `package.json`.

### Testing Strategies

Testing strategies are also simplified in a monorepo. This is because all parts of the system are in the same repo, so testing is easier and more consistent. Coordinating local serves to run automated e2e and integration tests against the system is easier because the repository will have the latest from each micro-frontend at all times.

These automated tests can be run in a CI/CD pipeline, which can be triggered by changes to the monorepo. This ensures that the full system is always tested and that any issues that were previously difficult to identify (integration points between micro-frontends) are caught early.

The test suites can also be written closer to how the user would actually use the final deployed application improving the quality of the final product.

## Where does Nx fit?

Nx is a powerful open-source build system that provides tools and techniques for enhancing developer productivity, optimizing CI performance, and maintaining code quality It works especially well with monorepos and comes with built-in support for Module Federation for Angular and React projects.

[Learn more about why Nx is the best choice for Module Federation tooling](./nx-for-module-federation)
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Using Nx for Module Federation
Coly010 marked this conversation as resolved.
Show resolved Hide resolved

Nx is a powerful open-source build system that works especially well with monorepos and comes with built-in support for Module Federation for Angular and React projects.

If you haven't already read the benefits of using a monorepo for Module Federation, make sure to read [Benefits of Module Federation in a Monorepo](./index) first.

## What is Nx?

Nx is a powerful open-source build system that provides tools and techniques for enhancing developer productivity, optimizing CI performance, and maintaining code quality.
It is the best tool for both building and maintaining monorepos as well as for managing Module Federation projects.

### Core Features of Nx

- Run Tasks Efficiently: Nx [runs tasks in parallel](https://nx.dev/features/run-tasks) and orders the tasks based on the dependencies between them.
- Distribute Tasks in CI: Nx scales your CI by [distributing tasks](https://nx.dev/ci/features/distribute-task-execution) across many VMs. Your CI is fast no matter how big your repository is.
- Cache Locally & Remotely: With [local](https://nx.dev/features/cache-task-results) and [remote caching](https://nx.dev/ci/features/remote-cache), Nx prevents unnecessary re-runs of tasks, saving you valuable dev time.
Coly010 marked this conversation as resolved.
Show resolved Hide resolved
- Split E2E Tests and Rerun Flaky Tests: Nx [automatically splits](https://nx.dev/ci/features/split-e2e-tasks) large e2e tests to distribute them across VMs. Nx can also automatically [identify and rerun flaky e2e tests](https://nx.dev/ci/features/flaky-tasks).
Coly010 marked this conversation as resolved.
Show resolved Hide resolved
- Automate Dependency Updates: if you leverage [Nx plugins](https://nx.dev/concepts/nx-plugins) you gain additional features such as [code generation](https://nx.dev/features/generate-code) and tools to [automatically upgrade](https://nx.dev/features/automate-updating-dependencies) your codebase and dependencies.
- Make it Your Own: Nx is highly customizable and extensible. Fine-tune it by [creating a plugin for your organization](https://nx.dev/extending-nx/tutorials/organization-specific-plugin) or [creating a tooling plugin](https://nx.dev/extending-nx/tutorials/tooling-plugin).

## Nx Module Federation Features

Nx providers out of the box support for Module Federation for Angular and React projects including projects with Server Side Rendering (SSR).
Coly010 marked this conversation as resolved.
Show resolved Hide resolved

### Generators for Scaffolding Module Federation Projects

Generators in Nx provide a consistent and convenient method for scaffolding projects in a monorepo. Nx Plugins provide generators for various frameworks and tools, including Module Federation.
Coly010 marked this conversation as resolved.
Show resolved Hide resolved
Rapidly scaffolding Module Federation projects for Angular and React requires just a single command. This is a great way to get started with Module Federation while also providing a consistent structure as you add more Producers (remotes) to your system.

You can see an example of the Nx `consumer` generator below:
```bash
# Generate an Angular Consumer application with three Producers
nx g @nx/angular:consumer shell --producers=products,cart,checkout

# Generate a React Consumer application with three Producers
nx g @nx/react:consumer shell --producers=products,cart,checkout
```

You can learn more about setting up Module Federation with Nx in the guides for [Angular](/practice/frameworks/angular/using-nx-for-angular) and [React](/practice/frameworks/react/using-nx-for-react).

### Executors

Nx provides executors for building and serving your Module Federation applications. The important one is the `module-federation-dev-server` executor which is used to serve your application locally. This executor provides the following features:

- Support for serving the Consumer (host) project along with all of its Producers (remotes) in a single command with the best resource management and DX.
- Support for serving individual Producers (remotes) that are currently being worked with HMR and file watching for faster local development.

You can learn more about how we [Scale Develop Experience](./nx-for-module-federation#scaling-developer-experience-dx) for Module Federation in Nx below.
Coly010 marked this conversation as resolved.
Show resolved Hide resolved

By forcing you to serve the Consumer (host) project along with all of its Producers (remotes) in a single command, Nx ensures that you get immediate feedback on any changes you make to the Producer (remote) projects providing immediate feedback if it impacts other projects in the system.
Coly010 marked this conversation as resolved.
Show resolved Hide resolved
However, it also allows you to [develop as a user](./nx-for-module-federation#develop-as-a-user) such that you follow the same flow that a user would follow using the deployed application.
Coly010 marked this conversation as resolved.
Show resolved Hide resolved

To learn more about how Nx achieves this with the `module-federation-dev-server` executor, see the [Nx Module Federation Technical Overview](https://nx.dev/concepts/module-federation/nx-module-federation-technical-overview).

### Scaling Developer Experience (DX)

To ensure fast development cycles by utilizing Nx's `module-federation-dev-server` executor, Nx ensures fast dev server startup times by:

- Providing a [local cache](https://nx.dev/features/cache-task-results) of the build artifacts for each project in the module federation system.
- Providing a [remote cache](https://nx.dev/ci/features/remote-cache) of the build artifacts for each project in the module federation system meaning that individual teams can take advantage of the cache produced by other teams. This is especially important when a team does not work on certain Producers (remotes).
- Running builds of Producers (remotes) in parallel speed up the build process. _(Cached artifacts will be used if available)_.
- Running only specified Producers (remotes) with the dev-server to limit the number of files that need to be watched.
- Splitting monolithic builds into smaller chunks and using the `affected` command to only run tasks for projects that were affected by changes.

_Note: Remote caching is provided by [Nx Cloud](https://nx.dev/nx-cloud)._

### Develop as a User

For both the best DX (Development Experience) and most accurate development of a Module Federation architecture Nx recommends viewing it as a single application. In other words, the Consumer (host) and all the Producers (remotes) are composed to form a single application.

The Consumer (host) is the entry point and the Producer (remotes) are modules used by the application. It just happens that the Producers (remotes) are fetched over-the-wire at runtime rather than being bundled into the application.

To support this, as well as to ensure a great local DX, Nx's Module Federation support is built in such a way that when developing locally you should always run serve on your Consumer (host) application. This will start up your full Module Federation architecture; serving your Consumer (host) with webpack-dev-server and each Producer (remote) via a single http-server. You can learn more about this in [Nx Module Federation Technical Overview](https://nx.dev/concepts/module-federation/nx-module-federation-technical-overview).

When you're working on a specific Producer (remote) application, you should use the `--devProducers` option to specify the Producer (remote) you are currently developing; e.g. `nx serve shell --devProducers=checkout`. This ensures that the Producer (remote) is served via webpack-dev-server allowing for HMR and live reloading.

## Use Cases Supported by Nx

### Micro-frontend Architecture

Nx recommends micro-frontend architectures for teams that require applications to be deployed independently. Independent Deployability is the concept where individual teams within an organization deploy their work on their own release cadence, regardless of other teams, allowing for more team autonomy. This can be achieved with Module Federation and becomes more and more appealing as the organization and application grows.

With Module Federation, each team can own a Producer (remote) that can be deployed when needed, and it will be consumed by the Consumer (host) application as expected, allowing for updates to that Producer (remote) to be made without the need to redeploy everything.
This lends itself to more of a Micro Frontend approach.

Learn more about [Micro Frontend Architecture](https://nx.dev/concepts/module-federation/micro-frontend-architecture) on Nx's documentation for more information.

### Faster Builds

As Module Federation allows you to split your application into smaller deployable chunks that are only required at runtime, you can take advantage of this to reduce the build times of your application.

You can run the builds of multiple smaller applications in parallel and deploy all of them together, maintaining a single release cadence and coordination across teams but benefiting with reduced build times locally for developers and in CI.

If you add [Nx Cloud](https://nx.dev/nx-cloud) to your Nx Workspace, then you can even get cache hits from some of the builds from other team members and CI, reducing the build time further.

Learn more about [Faster Builds](https://nx.dev/concepts/module-federation/faster-builds-with-module-federation) on Nx's documentation.

## Resources

- [Nx](https://nx.dev)
- [Nx Module Federation](https://nx.dev/concepts/module-federation)
- [[GUIDE]: Using Nx CLI for React](../frameworks/react/using-nx-for-react)
- [[GUIDE]: Using Nx CLI for Angular](../frameworks/angular/using-nx-for-angular)
Loading