In my opinion, Angular is the best choice for large enterprise applications. The basic project setup which is generated by using the Angular CLI is good but I prefer another way to set up a new project. In this blog post I want to talk about these topics:
- Using Nx instead of the Angular CLI
- TypeScript configuration
- Internationalization
- UI Component Explorer
- Domain-driven Design for your models
Nx
Nx is not a replacement for the Angular CLI but it uses the power of the Angular CLI and enhances it with additional tools. Anything you can do with the Angular CLI can also be done with Nx and you configure your project (as usual) with the angular.json
configuration file.
Basically, I love Nx due to these facts:
- I can easily integrate modern tools like Cypress, Jest and Prettier to my Angular project
- I can use effective development practices which are pioneered at Google, Facebook and Microsoft
Let us first talk about the usage of Cypress and Jest in Angular projects.
Why should I consider using Cypress instead of Protractor?
Check out this nice comparison to get more information about the differences between the two technologies.
Cypress is modern and interesting because it is not based on Selenium. Whereas Selenium executes remote commands through the network, Cypress runs in the same run-loop as your application. Additionally, it is fast and has nice features like:
- Time travel
- Debuggability
- Real-time reloads
- Automatic waiting
- Spies, stubs and clocks
- Network traffic control
- Consistent results
- Screenshots and videos
On the official feature website you can find further details to these features.
The greatest disadvantage of Cypress is, in my opinion, that it does not have full integration with tools like SauceLabs and BrowserStack and does not support other browsers than Chrome. This probably might change in the future but at the time of writing these features are not available yet.
In my opinion, Cypress is not a perfect choice for every Angular project but I would totally recommend that you should give it a try and make your own decision.
Why should I consider using Jest instead of jasmine?
In my experience, the testing experience using Karma + jasmine is worse when the projects become bigger:
- Slow build times (especially initially)
- Recompiling does not work reliably
- HTML reporter like karma-jasmine-html-reporter tend to be buggy
Jest was created by Facebook and is faster than other test runners because it is parallelizing tests. Additionally, it provides a CLI and has less configuration effort than other testing frameworks.
Some of the advantages of Jest compared to Karma + jasmine:
- Tests run faster as it can execute tests without building the whole app
- Using the CLI it is possible to filter by a filename or regex which reduces the need for
fdescribe
- Nearly no configuration needed to get started
- Stable tests
- The syntax is similar to jasmine
- Provides snapshot testing
- More active community
To be honest, I haven’t used Jest in any of my Angular projects yet but I will definitely give it a try in one of my next projects. The main reason why I haven’t used it yet is that I worked on existing codebases with many jasmine tests and there was no need/time/budget to migrate them to Jest.
Effective Development Practices
Using Nx you can work in a “monorepo” way of building your application. This approach is used by large software companies like Google, Facebook, Twitter and more to make it easier to work with multiple applications and libraries. These are some of the advantages of a monorepo approach:
- You commit a working piece of software which may include multiple parts like frontend and backend
- One toolchain setup
- Dependency management is easier
- Code can be split into composable modules
- Consistent developer experience
What I also like is the possibility to create applications and libraries in Nx which provide a nice way to structure larger applications:
- An application is anything that can run in the browser or on the server. It’s similar to a binary.
- A library is a piece of code with a well-defined public API. A library can be imported into another library or application. You cannot run a library.
As an example, it could be useful to define for each REST API endpoint a separate library which includes the Angular service and the corresponding models.
Additionally, you can enforce best practices in your project using workspace-specific schematics and code formatters.
See the official documentation to read more about how to use these mechanics in Nx.
TypeScript Configuration
I prefer to start with this tslint configuration as it uses the tslint configuration of Angular CLI and aligns with the Angular style guide.
In my tsconfig.json
file I enable strictNullChecks
which makes the code base more robust against possible null
or undefined
errors during runtime.
{
"compilerOptions": {
"strictNullChecks": true
}
}
From the official documentation:
In strict null checking mode, the null and undefined values are not in the domain of every type and are only assignable to themselves and any (the one exception being that undefined is also assignable to void).
Internationalization (i18n)
I configure internationalization from the beginning of a project even if the product is only planned for one country. This has two reasons:
- You get used to storing your translated texts in one file and not as hardcoded strings across the whole application
- If the application needs to get translated in another language you are prepared for it
I always use ngx-translate in my Angular projects especially as it provides to switch between languages during runtime of your application. This can become handy if you need to implement a language switcher in your app.
UI Component Explorer
If you develop your own components it can be helpful to create a custom view with all available components or use existing solutions like StoryBook.
In some projects, I created a separate page in the application (which was only visible to certain people) which showed a list of all available components. This page was used in manual testing sessions and provided a quick way to see if a new feature had an impact on any existing component. Additionally, it was possible to test the components in isolation.
Use Domain-driven Design for your models
One of the main ideas behind Domain-Driven Design is the separation of business logic (domain) from the rest of the application or implementation details. This can be easily implemented in Angular using TypeScript.
The goal of our domain model is to represent business logic. We want to avoid that certain business logic is split across multiple components and services but is available at a certain place. This way we can easily react and change the logic if something in the business requirement has changed.
An example of such a domain model could look like this:
export class User {
private firstName: string;
private lastName: string;
private age: number;
get firstName() {
return this.firstName;
}
get lastName() {
return this.lastName;
}
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
get age() {
return this.age;
}
constructor(firstName: string, lastName: string, age: number) {
this.setName(firstName, lastName);
this.setAge(age);
}
setName(firstName: string, lastName: string) {
if (this.validName(firstName) && this.validName(lastName)) {
this.firstName = firstName;
this.lastName = lastName;
}
}
setAge(age: number) {
if (age >= 18) {
this.age = age;
} else {
throw new Error('User age must be greater than 18');
}
}
private validName(name: string) {
if (name.length > 0 && /^[a-zA-Z]+$/.test(name)) {
return true
} else {
throw new Error('Invalid name format');
}
}
}
If, for example, the minimum age should be changed from 18 to 16 this logic needs only to be changed in this domain model class.
This article provides further details and a good approach to handle server-side business logic in your frontend application.
Conclusion
It is important to agree with your team on such an opinionated setup. I would propose this approach to the team, discuss alternatives, advantages, disadvantages and try to find a good compromise. In the end, the project should be scalable and the team should be able to quickly deliver features.
This article showed you my approach to set up a new Angular project. It is not complete and maybe not a perfect approach but it is my experience, so your suggestions are always welcome in the comments.
Top comments (4)
Will definitely dive into NX.
Jest is also on my todo list
Maybe consider compodoc to generate documentation of your application.
I've created an advanced starterkit with a lot of features you describe and more (SSR/Prerender), without a lot of boilerplate. You should check it out
github.com/rickvandermey/angular-s...
Yes, compodoc is really nice and a good choice for a documentation library!
Thanks for the nice write-up. One tip: the TypeScript team has announced that they are no longer supporting tslint. They advise everybody to move to eslint instead. See: eslint.org/blog/2019/01/future-typ...
Thanks for sharing! 🎉