Alright, fellow Angular adventurer! Welcome to the Monorepo Dojo. I'm your guide, ready to transform you from an Angular solo artist into an Nx Monorepo maestro. Forget managing multiple repositories, conflicting dependencies, and repetitive boilerplate. We're diving into a world of streamlined development, efficient code sharing, and powerful tooling with Nx.
Think of this as your personalized training program. We'll go step-by-step, building your understanding and skills along the way.
Our Roadmap:

  • Module 1: The "Why" - Monorepos & Nx Fundamentals
    • What's a Monorepo? Benefits & Challenges.
    • Enter Nx: The Monorepo Supercharger.
    • Key Nx Concepts: Workspace, Projects, Generators, Executors, Dependency Graph, Caching.
  • Module 2: Setting Up Your Nx Workspace
    • Installation and Workspace Creation.
    • Exploring the Workspace Structure (apps, libs, tools, config files).
    • Understanding nx.json and project.json.
  • Module 3: Your First Applications
    • Generating Your First Angular App.
    • Serving, Building, and Testing an App with Nx.
    • Generating a Second Angular App (Multi-app Power!).
  • Module 4: The Power of Shared Libraries
    • Why Shared Libraries are Awesome.
    • Generating Different Types of Libraries (UI, Data-Access, Utils, Feature).
    • Organizing Libraries Effectively (using directories).
  • Module 5: Connecting the Dots - Using Shared Libraries
    • Importing and Using Library Code in Your Apps.
    • TypeScript Path Mapping Magic (@myorg/...).
    • Visualizing Dependencies with the Dependency Graph (nx graph).
  • Module 6: Efficient Workflows & Tooling
    • Generating Components/Services within Apps and Libs.
    • The Magic of nx affected:* Commands (Build/Test/Lint only what changed).
    • Understanding Nx Caching (Local and Remote).
  • Module 7: Storybook Integration - Visual Component Development
    • Setting up Storybook for a UI Library.
    • Generating Stories for Your Components.
    • Running and Using Storybook.
  • Module 8: Tips, Tricks & Best Practices
    • Enforcing Boundaries & Linting Rules (@nx/enforce-module-boundaries).
    • Tagging Projects for Granular Control.
    • Structuring for Scalability.
    • Considering CI/CD implications.
  • Conclusion & Next Steps Let's begin! Module 1: The "Why" - Monorepos & Nx Fundamentals
  • What's a Monorepo?
    • Imagine putting all your related projects (frontend apps, backend services, shared libraries) into a single Git repository. That's a monorepo!
    • Benefits:
      • Visibility: Everyone sees all the code.
      • Code Sharing: Trivial to share code (like UI components, utility functions, data models) between projects.
      • Atomic Commits: Changes across multiple projects (e.g., updating a shared library and all its consumers) happen in a single commit.
      • Simplified Dependency Management: Often uses a single node_modules folder, reducing dependency conflicts.
      • Easier Refactoring: Refactoring shared code and its usages across the entire codebase is more straightforward.
    • Challenges (without good tooling):
      • Slow Builds/Tests: Running commands across everything can be slow if not optimized.
      • Code Ownership Clarity: Requires clear conventions.
      • Tooling Complexity: Standard tooling might struggle with multiple projects in one repo.
  • Enter Nx: The Monorepo Supercharger
    • Nx (Nrwl Extensions) is an open-source build system with first-class monorepo support and powerful integrations. It's designed to solve the challenges of monorepos.
    • Think of Nx as: A smart layer on top of your tools (Angular CLI, Jest, Cypress, Storybook, etc.) that understands your codebase structure and dependencies.
  • Key Nx Concepts:
    • Workspace: The root of your monorepo, containing all your code, configuration, and tooling.
    • Projects: Individual applications (apps/) or libraries (libs/) within the workspace.
    • Generators: Code scaffolding tools (nx generate ... or nx g ...) to create applications, libraries, components, services, etc., with pre-configured settings. Much more powerful than standard CLI generators in a monorepo context.
    • Executors (Targets/Builders): Define what can be done with a project (e.g., build, serve, test, lint). Configured in project.json. Nx runs these tasks.
    • Dependency Graph: Nx analyzes your code to understand which projects depend on which others. This is crucial for optimization.
    • Caching: Nx caches the results of tasks (like builds, tests). If you run the same task again on unchanged code, Nx replays the cached result instantly. Wizard Trick: This is a massive time saver, especially in CI! Module 2: Setting Up Your Nx Workspace
  • Installation and Workspace Creation:

    • You need Node.js and npm/yarn installed.
    • Open your terminal and run: npx create-nx-workspace@latest my-org-workspace
    • Nx will prompt you for configuration choices. For our purpose:
      • Choose angular as the preset (or angular-standalone if you prefer standalone components).
      • Enter your first application name (e.g., storefront).
      • Choose CSS, SCSS, etc., as you normally would.
      • Decide if you want Nx Cloud (free tier is great for caching, highly recommended!).
  • Exploring the Workspace Structure:

    my-org-workspace/

    ├── apps/ # Your runnable applications

    │ └── storefront/ # Example Angular app

    ├── libs/ # Your shared libraries (initially empty)

    ├── tools/ # Workspace-specific scripts, generators, etc.

    ├── nx.json # Nx workspace configuration

    ├── package.json # Project dependencies

    ├── tsconfig.base.json # Base TypeScript configuration

    └── workspace.json # (Older Nx versions) or project configs in apps/libs

    └── project.json # (In each app/lib in newer Nx versions) Project config

  • Understanding nx.json and project.json:

    • nx.json: Configures Nx itself. Defines things like the cache directory, default task runner options, plugins, target defaults, and potentially affected command bases (base and head).
    • project.json (inside each app/lib): Defines the project's specific configuration.
      • sourceRoot: Where the project's source code lives.
      • projectType: application or library.
      • targets: Defines the tasks (executors) you can run on this project (e.g., build, serve, test, lint). Specifies which executor (@nx/angular:webpack-browser, @nx/jest:jest, etc.) to use and its options.
      • tags: Used for defining boundaries and affected commands (more later). Module 3: Your First Applications
  • Generating Your First Angular App:

    • The create-nx-workspace command already created one (storefront in our example). Let's examine how you would do it manually.
    • Navigate into your workspace directory: cd my-org-workspace
    • Use the Nx Angular application generator: # Example using the @nx/angular plugin nx generate @nx/angular:application admin-portal --directory=apps/admin-portal --style=scss --routing=true --standalone=false # Or shorter: nx g @nx/angular:app admin-portal ...
      • @nx/angular:application: The generator name.
      • admin-portal: The name of your new app.
      • --directory: Explicitly puts it in apps/admin-portal. Good practice.
      • Other flags are standard Angular CLI flags passed through Nx.
  • Serving, Building, and Testing an App with Nx:

    • Nx proxies commands to the underlying tools, adding its optimizations.
    • Serve: nx serve storefront # Or to serve the second app: nx serve admin-portal

    Nx knows how to find the serve target in the project.json of the specified app.

    • Build: nx build storefront --configuration=production
    • Test: nx test storefront nx test admin-portal
    • Lint: nx lint storefront
    • Teacher Tip: Notice the pattern: nx [options].
  • Generating a Second Angular App:

    • We already did this with admin-portal. You now have apps/storefront and apps/admin-portal. This demonstrates the multi-app capability right from the start. Each app is independent but lives within the same workspace, ready to share code. Module 4: The Power of Shared Libraries
  • Why Shared Libraries?

    • This is where the monorepo magic happens! Instead of copy-pasting code or managing complex npm package links, you create libraries within your workspace.
    • Use Cases: UI components, data access logic (services, models), utility functions, state management features, domain-specific logic.
  • Generating Different Types of Libraries:

    • Nx encourages categorizing libraries by type using directories and tags. Common types:
      • feature: Smart components/pages specific to a feature.
      • ui: Dumb, reusable presentation components.
      • data-access: Services for fetching/managing data, state management code.
      • util: Generic utility functions.
    • Example Generators:

      Generate a library for shared UI components

      nx g @nx/angular:library ui-components --directory=shared/ui --buildable --publishable --importPath=@myorg/shared/ui

      Shorter alias: nx g @nx/angular:lib ui-components ...

Generate a library for core data access logic

nx g @nx/angular:lib core-data --directory=shared/data-access

Generate a library for authentication features

nx g @nx/angular:lib auth --directory=features/auth --lazy --routing

* --directory=shared/ui: Organizes the library under libs/shared/ui. Creates a clear structure.
 * --buildable: Creates a buildable library with its own package.json, suitable for separate compilation. Often used for UI libs or libs you might publish.
 * --publishable: Sets up the library to be publishable to npm (adds necessary build configurations). Requires --buildable.
 * --importPath: Defines the TypeScript path alias (e.g., @myorg/shared/ui). Makes imports clean. Nx configures tsconfig.base.json automatically.
 * --lazy --routing: For feature libraries, sets up lazy loading.
  • Organizing Libraries Effectively:
    • Use the --directory flag consistently.
    • Group by scope (shared, features, products/{product-name}) and then by type (ui, data-access, util).
    • Example structure: libs/ ├── shared/ │ ├── ui/ │ ├── data-access/ │ └── util/ ├── features/ │ ├── auth/ │ └── profile/ └── products/ └── cart/ ├── ui/ └── feature/

Module 5: Connecting the Dots - Using Shared Libraries

  • Importing and Using Library Code:
    • Once a library is generated, Nx automatically updates tsconfig.base.json with path mappings.
    • Example: Let's say you created a ButtonComponent in your shared/ui library (@myorg/shared/ui).
    • In your storefront app (e.g., app.component.ts or a feature module): // Import the component directly using the path alias import { SharedUiModule } from '@myorg/shared/ui'; // Assuming you export a module

// Or if exporting components directly (e.g., standalone):
import { ButtonComponent } from '@myorg/shared/ui';

@NgModule({
declarations: [AppComponent, /* ... /],
imports: [
BrowserModule,
SharedUiModule, // Import the library module
// or if standalone: ButtonComponent
/
... */
],
// ...
})
export class AppModule {}

// Or in a standalone component:
import { ButtonComponent } from '@myorg/shared/ui';

@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, ButtonComponent], // Import here
template:

Welcome to Storefront!



,
})
export class AppComponent {}

  • Wizard Trick: The path alias (@myorg/shared/ui) makes imports clean and independent of the library's actual location in the libs folder. Refactoring library locations becomes much easier!
    • Visualizing Dependencies with nx graph:
  • This is one of Nx's killer features! It shows you how your apps and libs depend on each other.
  • Run this command in your terminal:

    nx graph

  • This will likely open an interactive graph in your browser.

  • You can see lines connecting storefront to shared/ui, admin-portal to shared/ui, etc.

  • Teacher Tip: Use this graph frequently to understand your architecture and identify potential issues (e.g., unintended dependencies). You can also use it to visualize affected projects (nx affected:graph).
    Module 6: Efficient Workflows & Tooling

    • Generating Components/Services within Projects:
  • You can use Nx generators to add code inside specific apps or libs using the --project flag.

  • Example: Add a ProductCard component to the shared/ui library:
    nx g @nx/angular:component product-card --project=shared-ui --export

    Nx knows 'shared-ui' corresponds to the library in libs/shared/ui

    --export automatically adds it to the library's public API (index.ts)

  • Example: Add a UserService to the admin-portal app:
    nx g @nx/angular:service user --project=admin-portal --path=app/services

    • The Magic of nx affected:* Commands:
  • This is how Nx tackles the "slow builds/tests" problem in monorepos.

  • Nx uses the dependency graph and Git history (HEAD vs main or a specific base commit) to figure out which projects might have been affected by your recent code changes.

  • Commands:

    • nx affected:build: Builds only the apps/libs affected by changes.
    • nx affected:test: Tests only the projects affected by changes.
    • nx affected:lint: Lints only the projects affected by changes.
    • nx affected:graph: Visualizes the graph of affected projects.
  • Wizard Trick: In your CI/CD pipeline, instead of nx build my-app or nx test my-lib, you'll use nx affected:build --base=origin/main --head=HEAD (or similar). This drastically reduces CI run times!

    • Understanding Nx Caching:
  • When you run nx build my-app, Nx calculates a hash based on the project's source code, dependencies, and relevant configurations.

  • It stores the output (e.g., the dist/ folder) and terminal logs locally (node_modules/.cache/nx).

  • If you run nx build my-app again without changing anything relevant, Nx finds the matching hash in the cache and replays the output instantly (milliseconds vs. minutes!).

  • Nx Cloud: Extends this cache to your entire team and CI environment. If a teammate or CI job has already built/tested the exact same code, you can pull down the result from the cloud cache instead of running it locally.
    Module 7: Storybook Integration - Visual Component Development

    • Setting up Storybook for a UI Library:
  • Storybook is fantastic for developing UI components in isolation. Nx makes integration easy.

  • Let's add it to our shared-ui library:
    # Make sure you have the Storybook plugin (usually added with @nx/angular)

    If not: npm install -D @nx/storybook

nx g @nx/angular:storybook-configuration shared-ui

  • This command will:
    • Install necessary Storybook dependencies.
    • Create a .storybook folder inside the libs/shared/ui library with configuration files.
    • Add storybook and build-storybook targets to the library's project.json.
      • Generating Stories for Your Components:
  • Stories define how your components render in Storybook with different inputs.
  • Let's generate a story for the ProductCardComponent we created earlier:
    nx g @nx/angular:story product-card.component.ts --project=shared-ui

    Or: nx g @nx/angular:stories --project=shared-ui (to generate for all components in the lib)

  • This creates a product-card.component.stories.ts file next to your component. You'll edit this file to define different scenarios (e.g., card with image, card without image, card on sale).

    • Running and Using Storybook:
  • Start the Storybook development server:

    nx run shared-ui:storybook

  • This compiles your library and stories and opens Storybook in your browser. You can now interact with your ProductCardComponent (and others) in isolation, test different props, and ensure it looks correct.

  • Teacher Tip: Develop your UI components primarily in Storybook. It's faster and encourages creating truly reusable components.
    Module 8: Tips, Tricks & Best Practices

    • Enforcing Boundaries & Linting Rules:
  • You don't want your shared/ui library accidentally importing code from a specific feature/auth library. Nx helps enforce this.

  • Tags: Assign tags to projects in their project.json files:

    // libs/shared/ui/project.json

    {

    // ...

    "tags": ["scope:shared", "type:ui"]

    }

// libs/features/auth/project.json
{
// ...
"tags": ["scope:feature", "type:data-access"] // Or type:feature
}

// apps/storefront/project.json
{
// ...
"tags": ["scope:app", "type:app"]
}

  • ESLint Rule: Configure the @nx/enforce-module-boundaries rule in your root .eslintrc.json. Define rules like:

    • scope:app can only depend on scope:shared or scope:feature.
    • scope:feature can only depend on scope:shared.
    • type:ui cannot depend on type:data-access or type:feature. // .eslintrc.json (example snippet) "rules": { "@nx/enforce-module-boundaries": [ "error", { "enforceBuildableLibDependency": true, "allow": [], "depConstraints": [ { "sourceTag": "scope:shared", "onlyDependOnLibsWithTags": ["scope:shared"] }, { "sourceTag": "scope:feature", "onlyDependOnLibsWithTags": ["scope:feature", "scope:shared"] }, { "sourceTag": "scope:app", "onlyDependOnLibsWithTags": ["scope:app", "scope:feature", "scope:shared"] }, { "sourceTag": "type:ui", "onlyDependOnLibsWithTags": ["type:ui", "type:util"] } // Example: UI can't depend on data-access ] } ] }
  • Wizard Trick: This prevents architectural spaghetti and keeps your monorepo maintainable as it grows.

    • Structuring for Scalability:
  • Use clear naming conventions (directories, tags).

  • Keep libraries focused on a single responsibility.

  • Think about domain boundaries early on.

    • Considering CI/CD:
  • Leverage nx affected:* commands heavily.

  • Utilize Nx Cloud for distributed caching to speed up pipelines dramatically.

  • Set up separate deployment pipelines triggered by changes in specific applications.
    Conclusion & Next Steps
    Congratulations! You've journeyed through the fundamentals of Nx monorepos for Angular development.
    Key Takeaways:

    • Monorepos offer significant benefits for code sharing and consistency.
    • Nx provides the essential tooling (generators, executors, dependency graph, caching, affected commands) to make monorepos efficient and scalable.
    • You can generate multiple Angular apps and various types of shared libraries within one workspace.
    • Nx automatically handles TypeScript path mapping for clean imports.
    • The dependency graph (nx graph) is invaluable for understanding your architecture.
    • nx affected:* commands and caching are crucial for performance.
    • Storybook integrates seamlessly for isolated UI component development.
    • Tags and lint rules help enforce architectural boundaries. Your Next Steps:
    • Practice: Create a sample workspace. Generate apps and libs. Play with the commands. Break things and fix them!
    • Explore Official Docs: The Nx documentation (nx.dev) is excellent and constantly updated.
    • Dive Deeper: Look into specific Nx plugins (Node, React, NestJS, etc.), custom generators, advanced boundary rules, and Nx Cloud features.
    • Experiment: Try converting a small existing multi-repo project into an Nx monorepo to see the benefits firsthand.
    • Integrate: Start using these patterns in your next Angular project. You now have a solid foundation. Remember, mastery comes with practice and continuous learning. Go forth and build amazing things, efficiently! Good luck!