Enterprise Angular apps with Nx and monorepo approach

dtom@stibosystems.com

Problems we want to solve

  • Code formatting
  • Internal dependencies consistency
  • Responsibilities leak
  • External dependencies consistency
  • Code sharing
  • Sharing common practices in large teams
  • Code ownership
  • CI/CD scalability in large apps
  • and more...

Monorepo

Based on experience of

Monorepo !== Monolith

Monorepo

Polyrepo

Monolith

Modular

Why not ?

Problem: External dependencies consistency

Nx

Nx ❤️ Monorepo

But is not a "full" monorepo

initial-workspace/
    apps/            <-- All our apps (can be build and deployed separately)
    libs/            <-- All our libs (encapsulated code with own APIs)
    tools/           <-- All our tools (ex. local schematics) 
    .prettierrc      <-- Prettier configuration file
    angular.json     <-- Angular configuration file
    nx.json          <-- Nx configuration file
    package.json     <-- Single package.json per repository
    tsconfig.json    <-- Main tsconfig.json applied to all apps/libs
    tslint.json      <-- Main tslint.json applied to all apps/libs  
// equivalent commands 
npx create-nx-workspace initial-workspace
npm init nx-workspace initial-workspace
yarn create nx-workspace initial-workspace

application

library

Problem: Code formatting

// package.json
"lint-staged": {
    "*.{js,json,css,scss,md,ts,html,component.html}": [
        "prettier --write",
        "git add"
    ]
}
// package.json
// Check un-formatted files
"format:check": "./node_modules/.bin/nx format:check" 

// Overwrite un-formatted files
"format:write": "./node_modules/.bin/nx format:write" 

Problem: Code formatting

Problem: Internal dependencies consistency

npm run dep-graph

Trust but verify

Dependencies forbidden by default

// tslint.json
"nx-enforce-module-boundaries": [
  true, // Enable functionality
  {
    "allow": [], // Exceptions
    "depConstraints": [
      {
        // Explicit constraints
        "sourceTag": "*",
        "onlyDependOnLibsWithTags": ["*"]
      }
    ]
  }
]
// nx.json
"projects": {

    ...

    "rent-data-access": {
      "tags": ["scope:rent", "type:data-access"]
    },
    "shared-ui-buttons": {
      "tags": ["scope:shared", "type:ui"]
    }
    
    ...
}
// tslint.json
"nx-enforce-module-boundaries": [
  true,
  {
    "allow": [],
    "depConstraints": [
      {
        "sourceTag": "scope:payments",
        "onlyDependOnLibsWithTags": ["scope:payments", "scope:shared"]
      },
      {
        "sourceTag": "scope:rent",
        "onlyDependOnLibsWithTags": ["scope:rent", "scope:shared"]
      },
      {
        "sourceTag": "scope:shared",
        "onlyDependOnLibsWithTags": ["scope:shared"]
      },
      {
        "sourceTag": "type:ui",
        "onlyDependOnLibsWithTags": ["type:ui"]
      }
    ]
  }
]

Problem: Internal dependencies consistency

Problem: Responsibilities leak

Relative path

Missing service in index.ts

index.js

Problem: Responsibilities leak

Problem: Code sharing

Monorepo

+

Nx build in limitations

+

Nx Tags

+

Nx libs index.js

 

Problem: Code sharing

Problem: Sharing common practices in large teams

Prettier

+

Nx Schematics

+

Custom Schematics

Problem: Sharing common practices in large teams

Problem: Code ownership

Nx Libs

+

Nx Libs index.ts

+

Nx Libs Readme files

Problem: Code ownership

Problem: CI/CD scalability in large apps

npm run affected:build -- --base=master
npm run affected:test -- --base=master
npm run affected:e2e -- --base=master
npm run affected:dep-graph -- --base=master
npm run affected:lint -- --base=master

npm run affected:apps -- --base=master
npm run affected:libs -- --base=master
// test / build / e2e / dep-graph / lint
npm run affected:test -- --base=master --parallel --maxParallel=4
npm run affected:test -- --base=SHA1 --head=SHA2 
npm run affected:test -- --files=path/file1.ts,path/file2.ts
npm run affected:test -- --all
npm run affected:test -- --only-failed

Problem: CI/CD scalability in large apps

More...

Use new tools without configuration

// No Angular Only React workspace
npx create-nx-workspace happynrwl --preset=react

// React App in existing Empty/Angular/React workspace
ng g app frontend --framework=react
// Add node 'api' app for project 'my-project'
ng g node-app api --frontendProject=my-project
initial-workspace/
    apps/
        angular-app/            
        react-app/
        node-app/
        node-express-app/
        node-nest-app/
        typescript-app/

Add Nx to existing app

ng add @nrwl/schematics

Problems solved

  • Code formatting (Prettier)
  • Internal dependencies consistency (Nx tags)
  • Responsibilities leak (Nx libs)
  • External dependencies consistency (Monorepo)
  • Code sharing (Nx libs, Monorepo)
  • Sharing common practices in large teams (Schematics)
  • Code ownership (Nx libs)
  • CI/CD scalability in large apps (Nx affected)
  • Use new tools without configuration hell (Nx)
  • Add Nx to existing project

Thank You!

Nx

By Dariusz Tomaszewski