惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
Spread Privacy
Spread Privacy
T
Threatpost
C
Cisco Blogs
P
Palo Alto Networks Blog
AI
AI
Cyberwarzone
Cyberwarzone
NISL@THU
NISL@THU
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
Simon Willison's Weblog
Simon Willison's Weblog
T
Tor Project blog
Latest news
Latest news
AWS News Blog
AWS News Blog
D
Docker
S
SegmentFault 最新的问题
博客园 - 聂微东
WordPress大学
WordPress大学
Vercel News
Vercel News
S
Securelist
爱范儿
爱范儿
J
Java Code Geeks
Know Your Adversary
Know Your Adversary
S
Schneier on Security
Hugging Face - Blog
Hugging Face - Blog
F
Fortinet All Blogs
Last Week in AI
Last Week in AI
D
DataBreaches.Net
宝玉的分享
宝玉的分享
D
Darknet – Hacking Tools, Hacker News & Cyber Security
MongoDB | Blog
MongoDB | Blog
Engineering at Meta
Engineering at Meta
K
Kaspersky official blog
美团技术团队
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
量子位
博客园_首页
Attack and Defense Labs
Attack and Defense Labs
S
Secure Thoughts
Google Online Security Blog
Google Online Security Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
腾讯CDC
T
Threat Research - Cisco Blogs
雷峰网
雷峰网
有赞技术团队
有赞技术团队
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Privacy International News Feed
S
Security Affairs

Nx Blog

Sharing Tailwind CSS Styles Across Apps in a Monorepo | Nx Blog How SiriusXM Stays Competitive by Iterating and Getting to Market Fast | Nx Blog Agentic Experience Is the New Developer Experience | Nx Blog Nx Joins the Linux Foundation and the Agentic AI Foundation | Nx Blog A Monorepo Is NOT a Monolith | Nx Blog Why we deleted (most of) our MCP tools | Nx Blog Teach Your AI Agent How to Work in a Monorepo | Nx Blog How Broadcom stays efficient and nimble with monorepos | Nx Blog Why Monorepos are King in the Age of AI | Nx Blog Nx 2026 Roadmap: Expanding Agent Autonomy, Improving Performance, Better Polyglot and More | Nx Blog End to End Autonomous AI Agent Workflows with Nx | Nx Blog Autonomous Agents at Scale | Nx Blog Scaling 700+ Projects: How Nx Became a 'No-Brainer' for Caseware | Nx Blog Configure Tailwind v4 with Angular in an Nx Monorepo | Nx Blog The Missing Multiplier for AI Agent Productivity | Nx Blog A Year of Nx Webinars | Nx Blog Wrapping Up 2025 | Nx Blog Nx 22.3 Release: Angular 21 Support, tsgo Compiler, and Prettier v3 | Nx Blog Nx Cloud Release: Agent Resource Usage | Nx Blog Nx Platform Outperforms DIY Cache by 5x | Nx Blog An Nx Carol: Past, Present, and Future of Your Monorepo | Nx Blog Nx 22.1 Release: Terminal UI on Windows, Storybook 10, Vitest 4, and more! | Nx Blog The Compounding Effect: How Nx Features Multiply Performance Gains | Nx Blog 10 Monorepo Myths Debunked: Separating Fact from Fiction | Nx Blog Nx Cloud Release: Enterprise Task Analytics | Nx Blog Watch and Rebuild Storybook Dependencies with Nx | Nx Blog Book - React for Enterprise: Timeless Architecture for Enterprise Apps | Nx Blog Beyond Remote Cache: Unlock 70% More CI Performance | Nx Blog Nx 22 Release: Expanding the build platform | Nx Blog What's the Point of Generating All This Code If You Can't Merge It? | Nx Blog What's New in Nx Self-Healing CI | Nx Blog Nx Highlights: Smarter AI integration, all-new graph UI, and big new versions of your favorite tools | Nx Blog Making the Case for Smarter Monorepos, and How to Not Get Fooled by Myths | Nx Blog Integrating Biome in 20 Minutes | Nx Blog S1ngularity - What Happened, How We Responded, What We Learned | Nx Blog Stop Babysitting Your PRs: Self-Healing CI Cuts Time to Green by 50% | Nx Blog UKG Unifies Their Codebase and Eliminates CI Overhead to Focus on Customer Value | Nx Blog How Git Worktrees Changed My AI Agent Workflow | Nx Blog Nx Cloud Workspace Graph: See Your Organization's Code Structure Like Never Before | Nx Blog Seamless Java Deployment in Nx Using Docker | Nx Blog Getting Mobile Into Your Monorepo: Android + Nx | Nx Blog Polyglot Projects Made Easy: Integrating Spring Boot into an Nx Workspace | Nx Blog The Journey of the Nx Plugin for Gradle: From Prototype to Production | Nx Blog Combining Predictability and Intelligence With Nx Generators and AI | Nx Blog A New UI For The Humble Terminal | Nx Blog Continuous tasks are a huge DX improvement | Nx Blog New and Improved Module Federation Experience with Nx | Nx Blog A New UI for Nx Migration | Nx Blog Custom Task Runners and Self-Hosted Caching Changes | Nx Blog Enterprise Angular Monorepo Patterns | Nx Blog Using Rspack with Angular | Nx Blog Angular Architecture Guide To Building Maintainable Applications at Scale | Nx Blog Modern Angular Testing with Nx | Nx Blog Nx Update: 20.5 | Nx Blog Are Monorepos the Answer to Better AI-Assisted Development? | Nx Blog Making Cursor Smarter with an MCP Server For Nx Monorepos | Nx Blog React Development for 2025 | Nx Blog Using Apollo GraphQL in an Nx Workspace | Nx Blog Angular State Management for 2025 | Nx Blog Tailoring Nx for Your Organization | Nx Blog Nx Cloud Pipelines Come To Nx Console | Nx Blog Define the relationship with monorepos | Nx Blog See your affected project graph in Nx Cloud | Nx Blog Handling CORS In Your Workspace | Nx Blog Improve your architecture and CI pipeline times with Nx projects | Nx Blog Announcing Nx 20 | Nx Blog Introducing Nx Powerpack | Nx Blog Nx 19.5 is here! Stackblitz, Bun, Incremental Builds for Vite, Gradle Test Atomizer | Nx Blog Introducing Explain with AI | Nx Blog Nx Enterprise Podcast Episode 2: Tine Kondo | Nx Blog Monorepos and CI can be a Mess - Here's How Nx and Nx Cloud Fixed It | Nx Blog Nx Enterprise Podcast Episode 1: Hicham El Hammouchi | Nx Blog Nx 19.0 Release!! | Nx Blog Manage Your Gradle Project using Nx | Nx Blog Making the Argument for Monorepos | Nx Blog Reliable CI. A new execution model fixing both flakiness and slowness | Nx Blog Monorepos - Why Speed Matters | Nx Blog Nx Agents Walkthrough: Effortlessly Fast CI Built for Monorepos | Nx Blog Launch Nx Week Recap | Nx Blog Versioning and Releasing Packages in a Monorepo | Nx Blog Fast, Effortless CI | Nx Blog Introducing @nx/nuxt Enhanced Nuxt.js Support in Nx | Nx Blog What if Nx Plugins Were More Like VSCode Extensions | Nx Blog Monorepos: the Benefits, Challenges, and Importance of Tooling Support | Nx Blog Nx — Highlights of 2023 | Nx Blog Nx 17.2 Update | Nx Blog Unit Testing Expo Apps With Jest | Nx Blog Nx Docs AI Assistant | Nx Blog State Management Nx React Native/Expo Apps with TanStack Query and Redux | Nx Blog Nx 17 has Landed | Nx Blog Nx Conf 2023 — Recap | Nx Blog Nx Raises $16M Series A | Nx Blog Introducing Playwright Support for Nx | Nx Blog Nx 16.8 Release!!! | Nx Blog Step-by-Step Guide to Creating an Expo Monorepo with Nx | Nx Blog Qwikify your Development with Nx | Nx Blog Create Your Own create-react-app CLI | Nx Blog Storybook Interaction Tests in Nx | Nx Blog Evergreen Tooling — More than Just CodeMods | Nx Blog A Practical Guide on Effective AI Use - AI as Your Peer Programmer | Nx Blog
Taming Code Organization with Module Boundaries in Nx | Nx Blog
Miroslav Jonaš · 2021-12-18 · via Nx Blog

As your repository grows, it becomes more challenging to organize and name the applications and libraries. This organization, when done right, feels intuitive and allows team members to easily find projects and understand how they work together. When done poorly, it results in a mess we eventually end up calling "the legacy software". This article will show you different ways how you can prevent your repo from descending into chaos.

Our book Enterprise Angular Monorepo Patterns presents an in-depth guide to assist you with naming and organization. If you still haven't read this book, we warmly recommend you do. Don't let the name fool you, though — the architecture guidelines explained in this book apply to any framework.

On large projects, you will most likely find multiple teams working on different parts of the solution. Those projects are usually split into logical domains, where each team focuses on a single domain. Each domain block can have a clear public API which other domains can use to consume the information.

But the code organization is just one piece of the puzzle. The physical organization does not prevent developers from consuming the domains or parts of those domains, that should otherwise be outside of their reach. Nx ships with enforce-module-boundaries ESLint rule that helps restrict that possibility.

Understanding the default configuration

When you generate the first project in your workspace using one of our generators, one of the things you get for free is the full linter setup. The linter is preconfigured with a default ruleset that includes a set of best practices. Alongside the standard set of rules, the initial generated configuration includes a setup for enforce-module-boundaries rule.

{
  // ... default ESLint config here

  overrides: [
    {
      files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
      rules: {
        '@nrwl/nx/enforce-module-boundaries': [
          'error',
          {
            allow: [],
            depConstraints: [
              {
                sourceTag: '*',
                onlyDependOnLibsWithTags: ['*'],
              },
            ],
            enforceBuildableLibDependency: true,
          },
        ],
      },
    },

    // ... more ESLint overrides here
  ],
}

Let's dissect what each of these properties does.

The allow array acts as a whitelist listing the import definitions that should be omitted from further checks. You can read more about it in the Overriding the overrides section below.

The depConstraints section is the one you will be spending most time fine-tuning. It represents an array of constraints, each consisting of sourceTag and onlyDependOnLibsWithTags properties. The default configuration has a wildcard * set as a value for both of them, meaning that any project can import (depend on) any other project.

Note, the wildcard only applies to libraries. Applications and E2E applications cannot be imported. It wouldn't make any sense. If you want to combine applications, you should use the micro-frontends approach with the module federation.

The circular dependency chains such as lib A -> lib B -> lib C -> lib A are also not allowed. The self circular dependency (when lib imports from a named alias of itself), while not recommended, can be overridden by setting the flag allowCircularSelfDependency to true.

// ... more ESLint config here

"@nrwl/nx/enforce-module-boundaries": [
  "error",
  {
    "allowCircularSelfDependency": true,
    "depConstraints": [
      // ...list of constraints
    ]
  }
]

// ... more ESLint config here

Finally, the flag enforceBuildableLibDependency prevents us from importing a non-buildable library into a buildable one. You can read more on what buildable libraries are used for in our docs.

To best express the need for the boundaries and assist us through the explanation, we will be using the repository represented by the following graph:

The graph representation of the repository

Our repository consists of two applications — Store and Admin. Each of them is composed of several feature libraries — Products (for Store), Sales, and Invoices (for Admin). Also, both of these applications depend on the Core library, and every project in our repo depends on our Shared library. Using common sense, we would like to enforce certain boundaries:

  • A shared or core library should not be able to depend on a feature library
  • A feature library can depend on another feature library or a shared library

First, we will use the project configuration to annotate our projects with tags.

  • Tags used to live in nx.json but were in the recent version moved closer to the project, so you can locate them now in your project.json or workspace.json

Let's define the types of projects. We will use the following tags:

  • type:app for application
  • type:feature for feature library
  • type:util for utility library

Your changed project configuration should now have the tags section defined.

{
  // ... more project configuration here

  tags: ['type:app'],
}

Your enhanced graph will now look similar to this:

Graph with type tags set

The above list of library types is not complete. You might add specific ones for E2E projects or UI component libraries. Using the naming format type:* is just a suggestion. Consider this being a hashtag on your favorite social app. You can use any prefix or format you feel fitting. The important thing is that it's readable and intuitive to all the members of your team.

Now, that we have marked all of our projects, we can continue to define the rules in the root .eslintrc.json.

{
  // ... more ESLint config here

  '@nrwl/nx/enforce-module-boundaries': [
    'error',
    {
      // update depConstraints based on your tags
      depConstraints: [
        {
          sourceTag: 'type:app',
          onlyDependOnLibsWithTags: ['type:feature', 'type:util'],
        },
        {
          sourceTag: 'type:feature',
          onlyDependOnLibsWithTags: ['type:feature', 'type:util'],
        },
        {
          sourceTag: 'type:util',
          onlyDependOnLibsWithTags: ['type:util'],
        },
      ],
    },
  ],

  // ... more ESLint config here
}

Adding a second dimension

We said that a feature library can depend on any other feature library, but there is a small catch. Our two apps could be built with a different framework so mixing feature libraries would not be possible. To avoid any future impediments, we don't want to allow a feature library used in Store to depend on the feature library from Admin and vice versa. Additionally, only our apps should be able to load the Core library.

Project graph with type tags and technology badges

Let's add another dimension to allow such restrictions. We will define the necessary scope tags:

  • scope:store for store app-related projects
  • scope:admin for admin app related projects
  • scope:shared for shared projects
  • scope:core for core projects

Our diagram should now look like this:

Full project graph with two-dimensional tags

Let us now define our missing rules!

{
  // ... more ESLint config here

  '@nrwl/nx/enforce-module-boundaries': [
    'error',
    {
      // update depConstraints based on your tags
      depConstraints: [
        // ...previous project type related rules
        {
          sourceTag: 'scope:store',
          onlyDependOnLibsWithTags: [
            'scope:store',
            'scope:shared',
            'scope:core',
          ],
        },
        {
          sourceTag: 'scope:admin',
          onlyDependOnLibsWithTags: [
            'scope:admin',
            'scope:shared',
            'scope:core',
          ],
        },
        {
          sourceTag: 'scope:core',
          onlyDependOnLibsWithTags: ['scope:shared'],
        },
        {
          sourceTag: 'scope:shared',
          onlyDependOnLibsWithTags: ['scope:shared'],
        },
      ],
    },
  ],

  // ... more ESLint config here
}

Fine-grained external dependencies

You may want to constrain what external packages a project may import. In our example above, we want to make sure projects in the scope:store does not import any angular packages, and projects from the scope:admin do not import any react library. You can ban these imports using bannedExternalImports property in your dependency constraints configuration.

We can now enhance our rule configuration by providing additional information.

{
  // ... more ESLint config here

  '@nrwl/nx/enforce-module-boundaries': [
    'error',
    {
      // update depConstraints based on your tags
      depConstraints: [
        // ...previous project type related rules
        {
          sourceTag: 'scope:store',
          onlyDependOnLibsWithTags: [
            'scope:store',
            'scope:shared',
            'scope:core',
          ],
          // this covers all @angular pacakges
          bannedExternalImports: ['@angular/*'],
        },
        {
          sourceTag: 'scope:admin',
          onlyDependOnLibsWithTags: [
            'scope:admin',
            'scope:shared',
            'scope:core',
          ],
          // this covers react, but also react-router-dom or react-helmet
          bannedExternalImports: ['react*'],
        },
        {
          sourceTag: 'scope:core',
          onlyDependOnLibsWithTags: ['scope:shared'],
          bannedExternalImports: ['@angular/*', 'react*'],
        },
        {
          sourceTag: 'scope:shared',
          onlyDependOnLibsWithTags: ['scope:shared'],
          bannedExternalImports: ['@angular/*', 'react*'],
        },
      ],
    },
  ],

  // ... more ESLint config here
}

Using the wildcard * to match multiple projects e.g. react* we can save ourselves the effort of manually specifying every single project we want to ban.

Restricting transitive dependencies

Our solution doesn't contain only internal projects but also depends on various external NPM packages. These external dependencies are explicitly declared in our package.json. Unfortunately, a package is rarely an island. They often consist of a tree of transitive dependencies branching out leading to thousands of packages being installed in your node_modules folder. Although we have control over what version or which direct dependency we install, we have no control over what versions of what packages this dependency depends on. The transitive dependencies are often the source of our app's vulnerabilities. We can also never guarantee those dependencies will be there. Just by simply running npm install parent may get updated to a patch or minor version that would wipe out one of the transitive dependencies or replace it with one with breaking changes.

Therefore it's wise not to allow developers to import transitive dependencies in their projects. Our ESLint plugin provides a simple flag to turn this restriction on.

{
  // ... more ESLint config here

  '@nrwl/nx/enforce-module-boundaries': [
    'error',
    {
      // ... more rule config here
      banTransitiveDependencies: true,
    },
  ],

  // ... more ESLint config here
}

If you now try to import a transitive dependency, your linter responds with an error. This flag is disabled by default for now, but we highly recommend you enable it.

Overriding the overrides

Sometimes, we just need to override this configuration for a given project. The scenario for this might be testing or during the development, if we are unsure yet how a certain project will be tagged. While we strongly encourage you to plan your architecture carefully and never override the boundaries configuration, you still have an option to bale out and override it.

{
  // ... default ESLint config here

  overrides: [
    {
      files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
      rules: {
        '@nrwl/nx/enforce-module-boundaries': [
          'error',
          {
            // ignore any checks for these projects for now
            allow: ['a-wip-project', 'this-one-is-broken-so-ignore-it'],
            depConstraints: [
              // ...dependency constraints here
            ],
          },
        ],
      },
    },

    // ... more ESLint overrides here
  ],
}

Summary

Monorepos are often viewed only from the technical side, but they also bring a shift in human resources organization. Teams that were once isolated, now have to work together on the same solution.

Having a clean separation of concerns and well-defined cohesive units helps us scale our organization more easily and gives us more confidence in our architecture. not only does Nx provide tools to speed up the overall performance, but it provides tooling to enforce the organizational constraints in an automated way.

In this post, we listed several strategies which you can use to restrict your packages from being misused or use unplanned external resources. Use them thoroughly and on time, before things go out of hand.

Prefer a visual presentation over text? Then check out this talk recording by Juri Strumpflohner: https://www.youtube.com/watch?v=pER_Ak1yUaA&t=687s