Wassim Chegham PRO
Senior Developer Advocate @Microsoft ★ Angular contributor ★ Bazel contributor ★ GDE @Google ★ creator of @itsjustangular / hueaction.dev / ngx.tools / xlayers.dev / angular.run / thundr.dev
By Vincent OGLOBLINSKY & Wassim CHEGHAM
How To Be Successful With Your Next Angular Projects.
Web Architect at SII
GDE Web
ngx.tools, xlayers.dev, hexa.run
Sr. Cloud Advocate at MICROSOFT
GDE Web
Author of Compodoc
@vogloblinsky
@manekinekko
Angular Core Team
import foo from 'bar';
@NgModule({...})
export class AppModule {}
Project's internal APIs.
Interceptors, ExceptionHandler...
No business logic!
Local & global Shared APIs
Business logic.
Business logic only!
Modules, Components, Services, Pipes, Directives...
Modules, Providers, Components, Directives, Pipes, etc.
Avoid cluttering the global Shared Module. Use local shared modules.
Avoid putting Providers in the global Shared Module (Providers corruption!)
NgModules are going to be optional in the future.
export const routes: Route[] = [
{
path: '',
redirectTo: '/home',
pathMatch: 'full'
},
{
path: 'home',
loadChildren: () => import('../home/home.module')
.then(e => e.HomeModule)
},
{
path: 'editor',
loadChildren: () => import('./editor/editor.module')
.then(e => e.EditorModule)
},
{
path: 'upload',
loadChildren: () => import('./upload/upload.module')
.then(e => e.UploadModule)
},
{
path: '**',
redirectTo: '/home'
}
];
export const routes: Route[] = [
{
path: '',
redirectTo: '/home',
pathMatch: 'full'
},
{
path: 'home',
component: HomeComponent
},
{
path: 'editor',
component: EditorComponent
},
{
path: 'upload',
component: UploadComponent
},
{
path: '**',
redirectTo: '/home'
}
];
.
├── BUILD.bazel
├── app-routing.module.ts
├── app.component.html
├── app.component.ts
├── app.module.ts
├── billing
│ ├── BUILD.bazel
│ ├── billing.module.ts
│ ├── index
│ │ ├── index.component.html
│ │ ├── index.component.spec.ts
│ │ └── index.component.ts
│ ├── module0
│ │ ├── BUILD.bazel
│ │ ├── cmp0
│ │ │ ├── cmp0.component.html
│ │ │ ├── cmp0.component.scss
│ │ │ ├── cmp0.component.spec.ts
│ │ │ └── cmp0.component.ts
│ │ ├── cmp1
│ │ │ ├── cmp1.component.html
│ │ │ ├── cmp1.component.scss
│ │ │ ├── cmp1.component.spec.ts
│ │ │ └── cmp1.component.ts
│ │ └── module0.module.ts
│ └── module1
│ ├── BUILD.bazel
│ ├── cmp2
│ │ ├── cmp2.component.html
│ │ ├── cmp2.component.scss
│ │ ├── cmp2.component.spec.ts
│ │ └── cmp2.component.ts
│ ├── cmp3
│ │ ├── cmp3.component.html
│ │ ├── cmp3.component.scss
│ │ ├── cmp3.component.spec.ts
│ │ └── cmp3.component.ts
│ └── module1.module.ts
├── compute
│ ├── BUILD.bazel
│ ├── compute.module.ts
│ ├── index
│ │ ├── index.component.html
│ │ ├── index.component.spec.ts
│ │ └── index.component.ts
│ ├── module0
│ │ ├── BUILD.bazel
│ │ ├── cmp4
│ │ │ ├── cmp4.component.html
│ │ │ ├── cmp4.component.scss
│ │ │ ├── cmp4.component.spec.ts
│ │ │ └── cmp4.component.ts
│ │ ├── cmp5
│ │ │ ├── cmp5.component.html
│ │ │ ├── cmp5.component.scss
│ │ │ ├── cmp5.component.spec.ts
│ │ │ └── cmp5.component.ts
│ │ └── module0.module.ts
│ └── module1
│ ├── BUILD.bazel
│ ├── cmp6
│ │ ├── cmp6.component.html
│ │ ├── cmp6.component.scss
│ │ ├── cmp6.component.spec.ts
│ │ └── cmp6.component.ts
│ ├── cmp7
│ │ ├── cmp7.component.html
│ │ ├── cmp7.component.scss
│ │ ├── cmp7.component.spec.ts
│ │ └── cmp7.component.ts
│ └── module1.module.ts
├── datastore
│ ├── BUILD.bazel
│ ├── datastore.module.ts
│ ├── index
│ │ ├── index.component.html
│ │ ├── index.component.spec.ts
│ │ └── index.component.ts
│ ├── module0
│ │ ├── BUILD.bazel
│ │ ├── cmp8
│ │ │ ├── cmp8.component.html
│ │ │ ├── cmp8.component.scss
│ │ │ ├── cmp8.component.spec.ts
│ │ │ └── cmp8.component.ts
│ │ ├── cmp9
│ │ │ ├── cmp9.component.html
│ │ │ ├── cmp9.component.scss
│ │ │ ├── cmp9.component.spec.ts
│ │ │ └── cmp9.component.ts
│ │ └── module0.module.ts
│ └── module1
│ ├── BUILD.bazel
│ ├── cmp10
│ │ ├── cmp10.component.html
│ │ ├── cmp10.component.scss
│ │ ├── cmp10.component.spec.ts
│ │ └── cmp10.component.ts
│ ├── cmp11
│ │ ├── cmp11.component.html
│ │ ├── cmp11.component.scss
│ │ ├── cmp11.component.spec.ts
│ │ └── cmp11.component.ts
│ └── module1.module.ts
├── functions
│ ├── BUILD.bazel
│ ├── functions.module.ts
│ ├── index
│ │ ├── index.component.html
│ │ ├── index.component.spec.ts
│ │ └── index.component.ts
│ ├── module0
│ │ ├── BUILD.bazel
│ │ ├── cmp12
│ │ │ ├── cmp12.component.html
│ │ │ ├── cmp12.component.scss
│ │ │ ├── cmp12.component.spec.ts
│ │ │ └── cmp12.component.ts
│ │ ├── cmp13
│ │ │ ├── cmp13.component.html
│ │ │ ├── cmp13.component.scss
│ │ │ ├── cmp13.component.spec.ts
│ │ │ └── cmp13.component.ts
│ │ └── module0.module.ts
│ └── module1
│ ├── BUILD.bazel
│ ├── cmp14
│ │ ├── cmp14.component.html
│ │ ├── cmp14.component.scss
│ │ ├── cmp14.component.spec.ts
│ │ └── cmp14.component.ts
│ ├── cmp15
│ │ ├── cmp15.component.html
│ │ ├── cmp15.component.scss
│ │ ├── cmp15.component.spec.ts
│ │ └── cmp15.component.ts
│ └── module1.module.ts
├── hello-world
│ ├── BUILD.bazel
│ ├── hello-world.component.html
│ ├── hello-world.component.scss
│ ├── hello-world.component.spec.ts
│ ├── hello-world.component.ts
│ └── hello-world.module.ts
├── home
│ ├── BUILD.bazel
│ ├── home.html
│ └── home.ts
├── logging
│ ├── BUILD.bazel
│ ├── index
│ │ ├── index.component.html
│ │ ├── index.component.spec.ts
│ │ └── index.component.ts
│ ├── logging.module.ts
│ ├── module0
│ │ ├── BUILD.bazel
│ │ ├── cmp16
│ │ │ ├── cmp16.component.html
│ │ │ ├── cmp16.component.scss
│ │ │ ├── cmp16.component.spec.ts
│ │ │ └── cmp16.component.ts
│ │ ├── cmp17
│ │ │ ├── cmp17.component.html
│ │ │ ├── cmp17.component.scss
│ │ │ ├── cmp17.component.spec.ts
│ │ │ └── cmp17.component.ts
│ │ └── module0.module.ts
│ └── module1
│ ├── BUILD.bazel
│ ├── cmp18
│ │ ├── cmp18.component.html
│ │ ├── cmp18.component.scss
│ │ ├── cmp18.component.spec.ts
│ │ └── cmp18.component.ts
│ ├── cmp19
│ │ ├── cmp19.component.html
│ │ ├── cmp19.component.scss
│ │ ├── cmp19.component.spec.ts
│ │ └── cmp19.component.ts
│ └── module1.module.ts
├── monitoring
│ ├── BUILD.bazel
│ ├── index
│ │ ├── index.component.html
│ │ ├── index.component.spec.ts
│ │ └── index.component.ts
│ ├── module0
│ │ ├── BUILD.bazel
│ │ ├── cmp20
│ │ │ ├── cmp20.component.html
│ │ │ ├── cmp20.component.scss
│ │ │ ├── cmp20.component.spec.ts
│ │ │ └── cmp20.component.ts
│ │ ├── cmp21
│ │ │ ├── cmp21.component.html
│ │ │ ├── cmp21.component.scss
│ │ │ ├── cmp21.component.spec.ts
│ │ │ └── cmp21.component.ts
│ │ └── module0.module.ts
│ ├── module1
│ │ ├── BUILD.bazel
│ │ ├── cmp22
│ │ │ ├── cmp22.component.html
│ │ │ ├── cmp22.component.scss
│ │ │ ├── cmp22.component.spec.ts
│ │ │ └── cmp22.component.ts
│ │ ├── cmp23
│ │ │ ├── cmp23.component.html
│ │ │ ├── cmp23.component.scss
│ │ │ ├── cmp23.component.spec.ts
│ │ │ └── cmp23.component.ts
│ │ └── module1.module.ts
│ └── monitoring.module.ts
├── networking
│ ├── BUILD.bazel
│ ├── index
│ │ ├── index.component.html
│ │ ├── index.component.spec.ts
│ │ └── index.component.ts
│ ├── module0
│ │ ├── BUILD.bazel
│ │ ├── cmp24
│ │ │ ├── cmp24.component.html
│ │ │ ├── cmp24.component.scss
│ │ │ ├── cmp24.component.spec.ts
│ │ │ └── cmp24.component.ts
│ │ ├── cmp25
│ │ │ ├── cmp25.component.html
│ │ │ ├── cmp25.component.scss
│ │ │ ├── cmp25.component.spec.ts
│ │ │ └── cmp25.component.ts
│ │ └── module0.module.ts
│ ├── module1
│ │ ├── BUILD.bazel
│ │ ├── cmp26
│ │ │ ├── cmp26.component.html
│ │ │ ├── cmp26.component.scss
│ │ │ ├── cmp26.component.spec.ts
│ │ │ └── cmp26.component.ts
│ │ ├── cmp27
│ │ │ ├── cmp27.component.html
│ │ │ ├── cmp27.component.scss
│ │ │ ├── cmp27.component.spec.ts
│ │ │ └── cmp27.component.ts
│ │ └── module1.module.ts
│ └── networking.module.ts
├── registry
│ ├── BUILD.bazel
│ ├── index
│ │ ├── index.component.html
│ │ ├── index.component.spec.ts
│ │ └── index.component.ts
│ ├── module0
│ │ ├── BUILD.bazel
│ │ ├── cmp28
│ │ │ ├── cmp28.component.html
│ │ │ ├── cmp28.component.scss
│ │ │ ├── cmp28.component.spec.ts
│ │ │ └── cmp28.component.ts
│ │ ├── cmp29
│ │ │ ├── cmp29.component.html
│ │ │ ├── cmp29.component.scss
│ │ │ ├── cmp29.component.spec.ts
│ │ │ └── cmp29.component.ts
│ │ └── module0.module.ts
│ ├── module1
│ │ ├── BUILD.bazel
│ │ ├── cmp30
│ │ │ ├── cmp30.component.html
│ │ │ ├── cmp30.component.scss
│ │ │ ├── cmp30.component.spec.ts
│ │ │ └── cmp30.component.ts
│ │ ├── cmp31
│ │ │ ├── cmp31.component.html
│ │ │ ├── cmp31.component.scss
│ │ │ ├── cmp31.component.spec.ts
│ │ │ └── cmp31.component.ts
│ │ └── module1.module.ts
│ └── registry.module.ts
├── storage
│ ├── BUILD.bazel
│ ├── index
│ │ ├── index.component.html
│ │ ├── index.component.spec.ts
│ │ └── index.component.ts
│ ├── module0
│ │ ├── BUILD.bazel
│ │ ├── cmp32
│ │ │ ├── cmp32.component.html
│ │ │ ├── cmp32.component.scss
│ │ │ ├── cmp32.component.spec.ts
│ │ │ └── cmp32.component.ts
│ │ ├── cmp33
│ │ │ ├── cmp33.component.html
│ │ │ ├── cmp33.component.scss
│ │ │ ├── cmp33.component.spec.ts
│ │ │ └── cmp33.component.ts
│ │ └── module0.module.ts
│ ├── module1
│ │ ├── BUILD.bazel
│ │ ├── cmp34
│ │ │ ├── cmp34.component.html
│ │ │ ├── cmp34.component.scss
│ │ │ ├── cmp34.component.spec.ts
│ │ │ └── cmp34.component.ts
│ │ ├── cmp35
│ │ │ ├── cmp35.component.html
│ │ │ ├── cmp35.component.scss
│ │ │ ├── cmp35.component.spec.ts
│ │ │ └── cmp35.component.ts
│ │ └── module1.module.ts
│ └── storage.module.ts
├── support
│ ├── BUILD.bazel
│ ├── index
│ │ ├── index.component.html
│ │ ├── index.component.spec.ts
│ │ └── index.component.ts
│ ├── module0
│ │ ├── BUILD.bazel
│ │ ├── cmp36
│ │ │ ├── cmp36.component.html
│ │ │ ├── cmp36.component.scss
│ │ │ ├── cmp36.component.spec.ts
│ │ │ └── cmp36.component.ts
│ │ ├── cmp37
│ │ │ ├── cmp37.component.html
│ │ │ ├── cmp37.component.scss
│ │ │ ├── cmp37.component.spec.ts
│ │ │ └── cmp37.component.ts
│ │ └── module0.module.ts
│ ├── module1
│ │ ├── BUILD.bazel
│ │ ├── cmp38
│ │ │ ├── cmp38.component.html
│ │ │ ├── cmp38.component.scss
│ │ │ ├── cmp38.component.spec.ts
│ │ │ └── cmp38.component.ts
│ │ ├── cmp39
│ │ │ ├── cmp39.component.html
│ │ │ ├── cmp39.component.scss
│ │ │ ├── cmp39.component.spec.ts
│ │ │ └── cmp39.component.ts
│ │ └── module1.module.ts
│ └── support.module.ts
└── todos
├── BUILD.bazel
├── reducers
│ ├── BUILD.bazel
│ └── reducers.ts
├── todos.component.html
├── todos.component.scss
├── todos.component.ts
└── todos.module.ts
84 directories, 271 files
$ ng generate library my-awesome-logger
➜ scripts git:(master) / tree
.
├── clean-changelog.js
├── cloudbuild
│ ├── deploy.sh
│ ├── kubectl
│ │ ├── Dockerfile
│ │ ├── cloudbuild.yaml
│ │ ├── kubectl.bash
│ │ └── publish.sh
│ ├── ngcontainer
│ │ ├── Dockerfile
│ │ ├── cloudbuild.yaml
│ │ ├── nginx.conf
│ │ └── publish.sh
│ └── xlayers.template.yaml
├── github-create-comment.bash
├── local-build.sh
└── stamp-build.bash
$ ng add @company/deploy
import { ApiService } from '../../../core/api.service.ts';
import { CatsService } from '../../../services/cats.service.ts';
// tsconfig.json
{
"CompilerOptions": {
"baseUrl": "src",
"paths": {
"@core/*": ["app/core/*"]
"@services/*": ["app/services/*"]
}
}
}
import { ApiService } from '@core/api.service.ts';
import { CatsService } from '@services/cats.service.ts';
You should start investing in Schematics and Builders.
Components
Code Generation
Compiler *
(Parser, Lexer, AST)
VM Code
(Renderer *)
JIT Compilation
(run time)
AOT Compilation (build time)
* In v8: ViewEngine. In v9+: Ivy
Build in Prod mode (AOT) as often as possible.
Architect.ScheduleTarget()
ng build --prod
angular.json: defaultProject
angular.json: architect.build.builder
(e.g. @angular-devkit/build-angular:browser)
Create a Webpack plugin:
Common, Browser, Stats, Styles, Worker, Analytics, AOT or JIT.
browserBuild()
AOT && buildOptimizerLoader()
Architect.run()
new AngularCompilerPlugin() (@ngtools/webpack)
ngc (ViewEngine)
ngcc (Ivy)
Consider keeping up to date with the latest releases:
ng update [--next]
You should start investing in Bazel (demo!).
$ npm i webpack-bundle-analyzer -D
$ ng build --stats-json
$ webpack-bundle-analyzer dist/MyBundle/stats.json
Setup a bundles analyzer as part of your CI/CD.
You should consider the Component DevKit.
Avoid importing Fat Shared Modules in the TestBed Module!
this.entries$ = this.queries$.pipe(
map((query: string) => query ? query.trim() : ''),
filter(Boolean),
debounceTime(500),
distinctUntilChanged(),
switchMap((query: string) => this.fetchEntries(query)),
filterByOwnerType(OwnerType.User)
);
this.organizations$ = this.selectedRepository$.pipe(
map((repository) => repository && repository.owner.organizations_url),
switchMap((url: string | false) => {
return url ? this.fetchUserOrganizations(url) : of(undefined);
}),
);
Embrace Reactive Functional Programming.
Don't let one person HOLDS all the knowledge.
00. Avoid cluttering the global Shared Module. Use local shared modules.
02. NgModules are going to be optional in the future.
01. Avoid putting Providers in the global Shared Module.
03. You should start investing in Schematics and Builders.
04. Build in Prod mode as often as possible.
05. Consider keeping up to date with the latest releases.
06. You should start investing in Bazel.
07. Setup the bundles analyzer as part of your CI/CD.
08. You should consider the Component DevKit.
10. Embrace Reactive Functional Programming.
11. Don't let one person HOLDS all the knowledge.
09. Avoid importing Fat Shared Modules in the TestBed Module!
By Wassim Chegham
« Make it work, make it right, make it fast » - Kent Beck We all know this quote. But we sometimes stop at the first stage, lacking time or prioritization. Onboarding a new developer in a team is the best time to check if your codebase is « scalable and healthy ». We will explain in this talk some feedbacks from many years doing consulting on Angular projects with concrete use-cases : - what are the bad practices to avoid? - what are the opinionated choices of Angular which can help you? - regardless of the size of your team or your colleagues' experience, what are the best architectural choices you can do to enhance that?
Senior Developer Advocate @Microsoft ★ Angular contributor ★ Bazel contributor ★ GDE @Google ★ creator of @itsjustangular / hueaction.dev / ngx.tools / xlayers.dev / angular.run / thundr.dev