Nx Workspace and Monorepo Development
Nx is a build system specialized for monorepo management of large-scale applications including Angular. Adopted by companies like Google, Microsoft, and Cisco, it enables team boundary management, build acceleration, and code consistency.
1. What is Nx?
Overview and Philosophy
Nx is a smart, fast build system. It efficiently manages multiple projects within a monorepo and solves the following challenges:
- Reduced build times: Caching and impact analysis ensure only tasks related to changes are executed
- Easier code sharing: Simple library creation and sharing prevents code duplication
- Dependency management: Automatically detects inter-project dependencies and optimizes build order
- Consistency: Generators and boundary rules unify coding conventions across teams
Relationship with Angular CLI
Nx operates as an extension of Angular CLI. You use nx commands instead of ng commands, but internally it leverages Angular CLI functionality.
# Angular CLI
ng serve my-app
ng build my-app
# Nx (equivalent commands)
nx serve my-app
nx build my-app
Integrated Monorepo vs Package-Based Monorepo
| Approach | Description | Best For |
|---|---|---|
| Integrated | Nx centrally manages all projects. Unified build, test, and lint execution | New monorepos, Angular projects |
| Package-Based | Each package has its own package.json. Similar to npm/yarn workspaces | Gradual migration of existing projects |
Comparison with Turborepo
| Feature | Nx | Turborepo |
|---|---|---|
| Caching | Local + Remote (Nx Cloud) | Local + Remote (Vercel) |
| Code Generation | Generators (powerful) | None |
| Project Graph | Interactive visual graph | Basic graph |
| Module Boundaries | Enforceable via ESLint rules | None |
| Primary Target | Angular, React, Node.js, etc. | Primarily Next.js / React ecosystem |
2. Nx Workspace Setup
Creating a Workspace
npx create-nx-workspace@latest my-workspace
You'll be prompted to select:
- Workspace type: Angular (Integrated Monorepo)
- Application name: Name for the first application
- Style format: CSS / SCSS / Less, etc.
- Nx Cloud: Enable remote caching (recommended)
Workspace Structure
my-workspace/
├── apps/ # Applications
│ └── my-app/
│ ├── src/
│ ├── project.json # Project-specific configuration
│ └── tsconfig.json
├── libs/ # Shared libraries
│ └── shared/
│ └── ui/
├── nx.json # Global Nx config (caching, task settings)
├── tsconfig.base.json # Base TypeScript config
└── package.json
Adding Nx to Existing Angular Projects
# Add Nx to an existing Angular CLI project
npx nx init
Running nx init preserves the existing project structure while adding Nx's caching and task execution capabilities.
3. Creating Projects and Libraries
Creating an Application
nx generate @nx/angular:application my-new-app
Creating a Library
nx generate @nx/angular:library my-lib
Library Types and Naming Conventions
Nx best practices categorize libraries into the following types:
| Type | Directory | Purpose | Example |
|---|---|---|---|
| feature | libs/feature/ | Smart components, pages | feature-dashboard |
| ui | libs/ui/ | Presentational components | ui-button, ui-card |
| data-access | libs/data-access/ | API communication, state management | data-access-user |
| util | libs/util/ | Utility functions, helpers | util-date, util-format |
| model | libs/model/ | Interfaces, type definitions | model-user, model-order |
# Example: Create libraries for user features
nx generate @nx/angular:library feature-user-profile --directory=libs/feature
nx generate @nx/angular:library ui-avatar --directory=libs/ui
nx generate @nx/angular:library data-access-user --directory=libs/data-access
buildable vs publishable Libraries
| Option | Description | Use Case |
|---|---|---|
| buildable | Can be built independently. Better cache utilization | Used only within the monorepo |
| publishable | Can be published as an npm package | Libraries also used by external projects |
# Create a publishable library
nx generate @nx/angular:library shared-ui --publishable --importPath=@my-org/shared-ui
4. Project Graph (Dependency Visualization)
The nx graph Command
# Display interactive graph in browser
nx graph
The project graph automatically detects dependencies from import statements and TypeScript path aliases.
Use Cases
- Architecture overview: Visually grasp the overall project structure
- Circular dependency detection: Identify circular references on the graph
- Impact analysis: Show which projects are affected by changes to a specific library
# Show graph focused on a specific project
nx graph --focus=my-app
# Show graph of affected projects
nx affected:graph
5. affected Command (Impact Analysis)
The affected command analyzes Git diffs and runs tasks only on projects impacted by changes.
# Build only affected projects
nx affected -t build
# Test only affected projects
nx affected -t test
# Lint only affected projects
nx affected -t lint
Usage in CI/CD
- name: Run affected tests
run: npx nx affected -t test --base=origin/main --head=HEAD
By specifying comparison commits with --base and --head, only the minimum necessary builds and tests are run per PR. This dramatically reduces CI time even in large monorepos.
6. Caching and Remote Caching
Local Cache
Nx locally caches the results of tasks with identical inputs (source code + configuration). Subsequent runs skip execution and return results from cache.
# First run: Normal execution (seconds to minutes)
nx build my-app
# Second run: Cache hit (milliseconds)
nx build my-app
# > nx run my-app:build [local cache]
Nx Cloud Remote Cache
To share cache across the entire team, you can use Nx Cloud's remote caching.
# Connect to Nx Cloud
npx nx connect-to-nx-cloud
Remote cache benefits:
- Team member A's build results can be reused by member B
- CI execution results can be reused in local development
Distributed Task Execution (DTE)
Nx Cloud's distributed task execution automatically distributes tasks across multiple machines in CI pipelines.
- name: Start CI run
run: npx nx-cloud start-ci-run --distribute-on="5 linux-medium-js"
- name: Run tasks
run: npx nx affected -t lint test build
Cache Target Configuration
{
"targetDefaults": {
"build": {
"cache": true,
"dependsOn": ["^build"]
},
"test": {
"cache": true
},
"lint": {
"cache": true
}
}
}
7. Module Boundary Rules
@nx/enforce-module-boundaries
Using ESLint rules, you can automatically detect and prohibit invalid dependencies between projects. This is critical for maintaining architecture in large teams.
Project Classification with Tags
Set tags in each project's project.json:
{
"tags": ["type:feature", "scope:user"]
}
{
"tags": ["type:ui", "scope:shared"]
}
Dependency Rules with depConstraints
{
"rules": {
"@nx/enforce-module-boundaries": [
"error",
{
"depConstraints": [
{
"sourceTag": "type:feature",
"onlyDependOnLibsWithTags": ["type:ui", "type:data-access", "type:util", "type:model"]
},
{
"sourceTag": "type:ui",
"onlyDependOnLibsWithTags": ["type:ui", "type:util", "type:model"]
},
{
"sourceTag": "type:data-access",
"onlyDependOnLibsWithTags": ["type:data-access", "type:util", "type:model"]
},
{
"sourceTag": "type:util",
"onlyDependOnLibsWithTags": ["type:util", "type:model"]
},
{
"sourceTag": "type:model",
"onlyDependOnLibsWithTags": ["type:model"]
}
]
}
]
}
}
This configuration enforces the following rules:
- feature → Can depend on ui, data-access, util, model (cannot depend on other features)
- ui → Can depend on ui, util, model (cannot depend on data-access or feature)
- data-access → Can depend on data-access, util, model
- model → Can only depend on model
The React/Next.js ecosystem has no standard mechanism for enforcing module boundary rules. Large projects typically address this with restrictive eslint-plugin-import rules or custom ESLint rules.
8. Generators
Nx Generators vs Angular Schematics
| Feature | Nx Generators | Angular Schematics |
|---|---|---|
| Position | Part of Nx ecosystem | Part of Angular CLI |
| Target | Multi-framework: Angular, React, Node.js, etc. | Angular-specific |
| Testing | Easy unit testing with Jest | Somewhat complex |
| Dry Run | Preview with --dry-run | Same |
Creating Custom Generators
You can define team-specific code generation templates as generators.
# Create a generator plugin library
nx generate @nx/plugin:plugin my-plugin
# Add a custom generator
nx generate @nx/plugin:generator my-generator --project=my-plugin
Custom generators are used to unify team boilerplate and auto-generate new components and services with a consistent structure.
9. Micro Frontends with Nx
Nx supports building micro frontends using Module Federation.
Creating Host and Remotes
# Create the host application (shell)
nx generate @nx/angular:host shell --remotes=app1,app2
# Add a remote application
nx generate @nx/angular:remote app3 --host=shell
Module Federation Configuration
import { ModuleFederationConfig } from '@nx/webpack';
const config: ModuleFederationConfig = {
name: 'shell',
remotes: ['app1', 'app2'],
};
export default config;
Shared Library Management
- Nx automatically deduplicates shared libraries in Module Federation configuration
- Libraries in
libs/are shared as singletons, reducing bundle size - Each remote app can be built and deployed independently
10. CI/CD Pipeline Integration
GitHub Actions Configuration Example
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- uses: nrwl/nx-set-shas@v4
- run: npx nx affected -t lint test build
Key Points
fetch-depth: 0fetches complete Git history (needed for affected diff calculation)nrwl/nx-set-shasautomatically sets the base commitnx affectedtargets only projects affected by changes
11. Nx Console (VS Code Extension)
Nx Console is available as a VS Code extension, providing a GUI for Nx operations.
- Generator execution: Set options in a form interface to generate components, libraries, etc.
- Project graph: Display interactive dependency graphs within VS Code
- Task execution: Run build / serve / test / lint with one click
- affected confirmation: Visually display the impact scope of current changes
For detailed usage, see "VS Code Setup Best Practices".
12. Migrating Existing Projects
Gradual Migration from Angular CLI to Nx
Step 1: Add Nx
npx nx init
At this stage, the existing project structure remains unchanged. Nx's caching and task execution capabilities are added.
Step 2: Split into Libraries
Gradually split existing modules into Nx libraries.
# Create a shared UI library
nx generate @nx/angular:library shared-ui --directory=libs/ui
# Move existing code to the library
# → Update import paths
Step 3: Introduce Boundary Rules
Set tags on libraries and enable @nx/enforce-module-boundaries rules to ensure architectural consistency.
Migration Tips
- Don't try to migrate everything at once — proceed gradually
- Just benefiting from caching alone provides significant improvement
- Start library splitting with the most frequently changed modules
- Introducing
nx affectedin CI/CD pipelines early provides great benefits