Rules for structuring your code
// project.json for a library in NX{"name": "my-app-admin","$schema": "../../../../node_modules/nx/schemas/project-schema.json","projectType": "library","sourceRoot": "libs/my-app/feature-admin/src","prefix": "my-app","tags": ["type:feature", "scope:my-app", "platform:web"], // <-- Tags for boundaries"targets": {"test": {"executor": "@nx/jest:jest","outputs": ["{workspaceRoot}/coverage/libs/my-app/feature-admin"],"options": {"jestConfig": "libs/my-app/feature-admin/jest.config.ts"}},"lint": {"executor": "@nx/eslint:lint"}}}
app/applicationlibs/admin/ <-- Grouping Folderdata-access-adminfeature-adminui-adminutils-adminroster/ <-- Grouping Folderdata-access-adminfeature-rosterui-rosterutils-roster
app/applicationlibs/admin/data-access/ <-- Heavy Nestingadminfeature/detailsprofileshellui/adminutils/admin
libs/admin/data-access-admin <--Libraryfeature-admin <--Libraryprofileuser-importuser-listui-admin <--Libraryavatarlist-itemutils-admin <--Libraryhelpers
libs/admin/data-access <-- Everything is a libraryfeature-profilefeature-shellfeature-user-importfeature-user-listui-avatarui-list-itemutils-helpers
Use NX Generators to automatically add module boundaries to new libraries.
// npx nx g app-lib-generator admin/ui-user-details// npx nx g app-lib-generator admin/ui-user-details --libType=ui --scopeTarget=app1const projectName = options.name.replace(///g, '-');const rootComponentName = options.name.slice(options.name.lastIndexOf('/') + 1);const componentName = names(rootComponentName);const removeUnitTests = options.libType === 'model';const projectRoot = `libs/${options.scopeTarget}/${options.name}`;const importPath = `@app/${options.scopeTarget}/${options.name}`;const ngOptions = {prefix: 'app',name: `${options.scopeTarget}-${projectName}`,directory: projectRoot,importPath,tags: `type:${options.libType}, scope:${options.scopeTarget}`,skipModule: true,unitTestRunner: removeUnitTests ? UnitTestRunner.None : UnitTestRunner.Jest,standalone: true,strict: true,flat: true};await angularLibraryGenerator(tree, ngOptions);// Remove unnecessary files
"rules": {"@nrwl/nx/enforce-module-boundaries": ["error",{enforceBuildableLibs: true,depConstraints: [{sourceTag: "type:ui",onlyDependOnLibsWithTags: ["type:ui", "type:utils"]},{sourceTag: "type:feature",onlyDependOnLibsWithTags: ["type:feature", "type:ui", "type:data-access", "type:utils"]},{sourceTag: "type:data-access",onlyDependOnLibsWithTags: ["type:data-access", "type:utils"]}]}]}
{"rules": {"@nrwl/nx/enforce-module-boundaries": ["error",{enforceBuildableLibs: true,depConstraints: [{sourceTag: "scope:application1",onlyDependOnLibsWithTags: ["scope:application1", "scope:shared"]},{sourceTag: "scope:application2",onlyDependOnLibsWithTags: ["scope:application2", "scope:shared"]},{sourceTag: "scope:shared",onlyDependOnLibsWithTags: ["scope:shared"]},]}]}}
{"rules": {"@nrwl/nx/enforce-module-boundaries": ["error",{enforceBuildableLibs: true,allow: ['@shared/**', '@features/**', '@store/**', '@services/**'], // Migration strategydepConstraints: [{sourceTag: "scope:shared",notDependOnLibsWithTags: ["scope:application1", "scope:application2"]},{allSourceTags: ["scope:admin", "type:ui"],onlyDependOnLibsWithTags: ["type:util"],allowedExternalImports: ["@angular/core", "@angular/common"] // Allow Angular importsbannedExternalImports: ["*router*", "@angular/common/http"] // Rejects Angular Router & http imports}]}]}}
apps/admin/components/services/store/admin.componentroster/
apps/roster/libs/admin/feature-admin/ <-- Feature Libcomponents/services/store/admin.component
apps/roster/libs/admin/feature-admin/componentsservicesstoreadmin.component
apps/roster/libs/admin/data-access-admin/ <-- previously services and storefeature-admin/admin.componentui-admin/ <-- Previously components
import {noDependencies, sameTag, SheriffConfig } from '@softarc/sheriff-core';export const sheriffConfig: SheriffConfig = {modules: {// Manual configuration of modules'src/app/admin/feature': ['scope:admin', 'type:feature'],'src/app/admin/data': ['scope:admin', 'type:data'],'src/app/roster/feature': ['scope:roster', 'type:feature'],'src/app/roster/data': ['scope:roster', 'type:data'],'src/app/<scope>/<type>': ['scope:<scope>', 'type:<type>']},depRules: {root: ['*'],'scope:*': [sameTag, 'scope:shared'],'type:feature': ['type:ui', 'type:data', 'type:util'],'type:ui': ['type:data', 'type:util'],'type:data': ['type:util'],'type:util': noDependencies,},};
const sheriff = require('@softarc/eslint-plugin-sheriff');module.exports = tseslint.config(// ... previous rules{files: ['**/*.ts'],extends: [sheriff.configs.all],},);
npx sheriff init // Creates your sheriff.config.tsnpx sheriff verify main.ts // Verifies your module boundaries