From 7672020c37b2b11df36efd9a46cf294ea47b9a93 Mon Sep 17 00:00:00 2001 From: Zain Zafar Date: Wed, 9 Jan 2019 17:26:30 +0500 Subject: [PATCH 1/5] add LIFT patterns and File Imports section to spa-application-architecture file --- .../spa-applications-architecture.md | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/frontend/architecture/spa-applications-architecture.md b/frontend/architecture/spa-applications-architecture.md index b445195..33d580e 100644 --- a/frontend/architecture/spa-applications-architecture.md +++ b/frontend/architecture/spa-applications-architecture.md @@ -2,11 +2,50 @@ This particular guide focus on what to keep in mind while designing the architecture of the new Single Page Application that you are going to develop. -1. Maintainable CSS -2. Multiples components/entities in one file -3. Use of LIFT pattern -4. Server Side Rendering for public pages -5. Public and Private pages -6. State Management -7. Best Architecture is independent of the frameworks +## Use of LIFT pattern + +This pattern is listed on Angular's Official [Guide](https://angular.io/guide/styleguide#lift) + +LIFT is an acronym for **L**ocate, **I**dentify, **F**lat, **T**-DRY. + +**Locate**: should be able to locate files quickly. Keep relative files near each other. For example `PassengerDetails` component should be placed near `PassengerList`. + +**Identify**: should be able to identify files based on their name and what it contains or does. For example adding `.spec` isn't a requirement in naming your test files but helps developer in identifying that it contains test logic. + +**Flat**: should be able navigate to easily, avoid nesting directories needlessly. Rule of thumb is to create a sub-directory when files count reaches 7. + +**T-DRY** (Try to be Don't Repeat Yourself): should not repeat yourself. It makes sense to be DRY but in some cases you should avoid being DRY. For example instead of naming `user-details-test.spec.js` can be written as `user-details.spec.js`, because we already know that .spec is a test file and we don't need to repeat that in the name of the file. + +## File Imports +Sort imports alphabetically (destructered imports as well), leave a space between imports so that you might able to distinguish between 3rd Party and Application imports. Increases readability when imports become large in number. + + +**Not Recommended** +```js +import React from 'react'; +import App, { Container } from 'next/app'; +import { registerSuccess, loginSuccess } from '../redux/actions'; +import BookingDetails from '../components/bookingDetails'; +``` + +**Recommended** +```js +import App, { Container } from 'next/app'; +import React from 'react'; + +import BookingDetails from '../components/bookingDetails'; +import { loginSuccess, registerSuccess } from '../redux/actions'; +``` + +## Lazy Loading + +## Maintainable CSS + +## Multiple entities/components in single file + +## State Management + +## Server Side Rendering + +## Best architecture is independent of the framework From a5102d3968d8c9eb10a59c622b01dc362e6bafbb Mon Sep 17 00:00:00 2001 From: Zain Zafar Date: Wed, 9 Jan 2019 19:16:26 +0500 Subject: [PATCH 2/5] add `npm start` script for development in `package.json` --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 20fa880..adcef96 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "http-server": "^0.11.1" }, "scripts": { + "start": "vuepress dev", "dev": "vuepress dev", "build": "vuepress build", "serve": "http-server .vuepress/dist" From da143138931ab70fd6581ff2c018b4cf488dbc5f Mon Sep 17 00:00:00 2001 From: Zain Zafar Date: Wed, 9 Jan 2019 21:14:03 +0500 Subject: [PATCH 3/5] complete spa-applications-architecture post. --- .../spa-applications-architecture.md | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/frontend/architecture/spa-applications-architecture.md b/frontend/architecture/spa-applications-architecture.md index 33d580e..2050c51 100644 --- a/frontend/architecture/spa-applications-architecture.md +++ b/frontend/architecture/spa-applications-architecture.md @@ -17,10 +17,11 @@ LIFT is an acronym for **L**ocate, **I**dentify, **F**lat, **T**-DRY. **T-DRY** (Try to be Don't Repeat Yourself): should not repeat yourself. It makes sense to be DRY but in some cases you should avoid being DRY. For example instead of naming `user-details-test.spec.js` can be written as `user-details.spec.js`, because we already know that .spec is a test file and we don't need to repeat that in the name of the file. ## File Imports -Sort imports alphabetically (destructered imports as well), leave a space between imports so that you might able to distinguish between 3rd Party and Application imports. Increases readability when imports become large in number. +Sort imports alphabetically (destructered imports as well), leave a space between imports so that you might able to distinguish between 3rd Party and Application imports. Increases readability when imports become large in number. **Not Recommended** + ```js import React from 'react'; import App, { Container } from 'next/app'; @@ -29,6 +30,7 @@ import BookingDetails from '../components/bookingDetails'; ``` **Recommended** + ```js import App, { Container } from 'next/app'; import React from 'react'; @@ -39,13 +41,44 @@ import { loginSuccess, registerSuccess } from '../redux/actions'; ## Lazy Loading +Lazy loading or on-demand loading of files/components/modules is a methodology to improve your application's performance. YES! Lazy sounds not so fast but what it actually means that if you application has 10 modules, all of those are not going to be fetched on initial app load but lazily. SPA frameworks today offer this feature out of the box. + +How does it work? +Bundler splits your code into multiple files these files are then fetched according to the routes they are associated with. + +Do I need lazy loading? +Off course, it will boost your applications performance like crazy. Imagine have 100 different components across the application whereas the page the user is requesting only uses 5 of these components. With a lazy loaded app you'll only need to fetch those 5 components and not 100. It's clear win-win situation, you are not only boosting the performance of your application but saving a lot of bandwidth as well. + ## Maintainable CSS -## Multiple entities/components in single file +SPA frameworks enable you to write CSS that can be scoped only to the component it is associated with. It makes the component fully independent and can be exported as a plugin easily which makes it re-usable. The idea of using scoped CSS looks amazing for creating external plugins but there are some pitfalls of using this approach thoughout the application. Designers should be responsible for writing CSS, and not developers. + +If there are new changes in a page, to incorporate those changes designer will either have to understand the underlying framework used or have to constantly interact with developer to incorporate the new styles into that page/component. Since both approaches might not be feasible in every case it's better to keep your application level CSS apart from the underlying framework. And a designer should have full control over styling of the application. + +## Multiple entities/components in a single file + +Having components separated out in their own files is the recommended approach. It helps you follow the Single Responsibility Priciple easily, code is manageble and maintenance becomes easier. However there might be case where you want to keep similar looking components in a single file or any other reason of the sort, like in alert-component file you may want to export info, danger and warning component etc. ## State Management +State Management is the `Single Source of Truth` in your application. Changes in your application not occur because of AJAX requests but there are states for indicating whether the spinner is running or the modal is opened etc. all of these has nothing to do with the API your app is connected with. So how do manage state changes within the application? Since data is changing over the time efficiently and these changes in state of SPA application can cause re-rendering, which is expensive. + +To make the state predictable and change only when needed you have take out the state from the components and bind it with universal store across your SPA application that listens to the actions triggered and mutate the state only where needed. + +There are number for solutions for every SPA framework out there but one of the most popular solution for state management is [Redux](https://redux.js.org/). One of the benefit of using it is that it's framework agnostic. + ## Server Side Rendering -## Best architecture is independent of the framework +SEO in Single Page Applications comes as an afterthought. A frontend architecture should it make SEO easier rather than hinder it. The developer should not go for third party solutions or create a completely separate application in order to handle SEO. Server Side Rendered application is often referred to as `Isomorphic` or `Universal` application. SPA frameworks in 2019 are evolving very fast and support SSR. + +There are three main reasons to create a Universal version of your app. + +1. Facilitate web crawlers (SEO) +2. Improve performance on mobile and low-powered devices +3. Show the first page quickly + +Universal or Server Side Rendered solutions for most popular frameworks are listed below: +- Angular :point_right: [Angular Universal](https://angular.io/guide/universal) +- ReactJS :point_right: [Next JS](https://nextjs.org/) +- VueJS :point_right: [Nuxt JS](https://nuxtjs.org/) From 48a84fa9e7c4e98db932643c2d2c735c9e83f28a Mon Sep 17 00:00:00 2001 From: Zain Zafar Date: Wed, 16 Jan 2019 14:10:13 +0500 Subject: [PATCH 4/5] Add `Angular Code Guidelines` section --- .vuepress/config.js | 1 + frontend/js-frameworks/angular-code-guidelines.md | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 frontend/js-frameworks/angular-code-guidelines.md diff --git a/.vuepress/config.js b/.vuepress/config.js index ce1fd7a..7a028ee 100644 --- a/.vuepress/config.js +++ b/.vuepress/config.js @@ -35,6 +35,7 @@ module.exports = { children: [ '/frontend/js-frameworks/javascript-frameworks-seo-challenges', '/frontend/css/css-best-practices', + '/frontend/js-frameworks/angular-code-guidelines', '/frontend/architecture/spa-applications-architecture', '/frontend/architecture/micro-frontends' ] diff --git a/frontend/js-frameworks/angular-code-guidelines.md b/frontend/js-frameworks/angular-code-guidelines.md new file mode 100644 index 0000000..fb39271 --- /dev/null +++ b/frontend/js-frameworks/angular-code-guidelines.md @@ -0,0 +1,5 @@ +# Angular Code Guidelines + +Building an application and building an application with manageable/scalable code are 2 different things. Despite being an opinionated framework Angular requires some practices on developer's end to be adopted so that the code written is maintainable and scalable. + +Every Angular developer must go through [Angular's Official Style Guide](https://angular.io/guide/styleguide) at least once. It's very well written with recommendations including What to **DO**, **AVOID** and **CONSIDER**. \ No newline at end of file From b9589c62011cb49d40226f1185dca6f423b9b03f Mon Sep 17 00:00:00 2001 From: Zain Zafar Date: Wed, 16 Jan 2019 18:51:56 +0500 Subject: [PATCH 5/5] add `Angular Code Guidelines` section --- .../spa-applications-architecture.md | 6 + .../js-frameworks/angular-code-guidelines.md | 143 +++++++++++++++++- 2 files changed, 148 insertions(+), 1 deletion(-) diff --git a/frontend/architecture/spa-applications-architecture.md b/frontend/architecture/spa-applications-architecture.md index 2050c51..4e95d0a 100644 --- a/frontend/architecture/spa-applications-architecture.md +++ b/frontend/architecture/spa-applications-architecture.md @@ -59,6 +59,12 @@ If there are new changes in a page, to incorporate those changes designer will e Having components separated out in their own files is the recommended approach. It helps you follow the Single Responsibility Priciple easily, code is manageble and maintenance becomes easier. However there might be case where you want to keep similar looking components in a single file or any other reason of the sort, like in alert-component file you may want to export info, danger and warning component etc. +## API Calls & Response + +- When using `Observables` it's easier lose hold of the subsriber which might lead to redundent calls if not handled properly. So make sure to open your network tab in browser and check if there are any redundent/duplicate call being made without any reason. +- Coordinate with Backend developers for the response being sent, if you are receiving redundent data, report it to the Backend team and get it trimmed, it will help in reducing the payload and in result quicker response from the APIs. +- Request body should not contain any empty string. Javascript treats empty string as false value but JAVA does not. + ## State Management State Management is the `Single Source of Truth` in your application. Changes in your application not occur because of AJAX requests but there are states for indicating whether the spinner is running or the modal is opened etc. all of these has nothing to do with the API your app is connected with. So how do manage state changes within the application? Since data is changing over the time efficiently and these changes in state of SPA application can cause re-rendering, which is expensive. diff --git a/frontend/js-frameworks/angular-code-guidelines.md b/frontend/js-frameworks/angular-code-guidelines.md index fb39271..af3a026 100644 --- a/frontend/js-frameworks/angular-code-guidelines.md +++ b/frontend/js-frameworks/angular-code-guidelines.md @@ -2,4 +2,145 @@ Building an application and building an application with manageable/scalable code are 2 different things. Despite being an opinionated framework Angular requires some practices on developer's end to be adopted so that the code written is maintainable and scalable. -Every Angular developer must go through [Angular's Official Style Guide](https://angular.io/guide/styleguide) at least once. It's very well written with recommendations including What to **DO**, **AVOID** and **CONSIDER**. \ No newline at end of file +Every Angular developer must go through [Angular's Official Style Guide](https://angular.io/guide/styleguide) at least once. It's complete set of rules and is very well written with recommendations including What to **DO**, **AVOID** and **CONSIDER**. And I'll be discussing some of the topics from the guide breifly. + +## Variables, Properties & Methods + +- Use `let` & `const` instead of `var` as var is function scoped whereas let and const are blocked scoped. let is immutable whereas const isn't. +- Don't use \_ (underscore) for private methods or properties as Typescript has it covered. +- Place `private` members after `public` members, make sure they are in alphabetic order. (I place ngOnDestroy at the end of file as I find it convenient because that's where the clean up of component takes place normally) +- Write small methods - no more than **75** lines of code +- Specify properties and variables types + +## LIFT Pattern + +Read this: [LIFT Pattern in Single Page Applications](/frontend/architecture/spa-applications-architecture.html#use-of-lift-pattern). + +## Comments + +Make a practice of adding comments on top of your methods. + +```ts +/** + * @description Checks if the user is verified or not + * + * @param string username - the username to be verified + * @return boolean - verified or not +**/ +checkUserVerification(username: string): boolean { /*...*/ return isVerified; } +``` + +## Imports + +Read this: [File Imports in Single Page Applications](/frontend/architecture/spa-applications-architecture.html#file-imports). + +More on imports + +- Import only what you need +- Remove usage of a service from a component or any other service remove it's import as well. + +## Take TSLint Seriously + +Developers tend to ignore errors or suggestion highlighted by TSLint, which creates proplems in consistency. This might lead Git to show change by the user who actually never worked on that part of the file. Just because other developer used double quotes instead of single and TSLint changes those quotes to single automatically for the new developer. + +Plus TSLint adds squigly lines that makes the code look uglier. + +Make sure you either use Linter properly or write a code that never wakes the Linter :ghost: + +Follow are some basic rules to avoid TSLint errors popping up all across your file. + +- Prefer use of `const` instead `let` where possible. +- Avoid using `var` keyword. +- Use single quotations marks. +- Keep opening curly brace on the same line. +- Enforce curly braces for if/for/do/while statements. +- Enforce indentation with tabs or spaces. + - Using only one of tabs or spaces for indentation leads to more consistent editor behavior, cleaner diffs in version control. +- Line should be under max length, recommended length is 140 characters. + - Break statements to multi lines, if it exceeds one line to make code more readable. +- As mentioned earlier order should be maintained in members of the file i.e. Static > Public > Private and in alphabetical order +- Append colon at the end of each statement +- Use strict comparison i.e. triple equals +- Avoid unnecessary white spaces + +## Single Responsibility Principle + +Makes your code easier to read and manage and more convenient to test. Consider limiting your components to **400** lines at max. Code is less prone to errors. + +## Enums + +One of the benefits of using Typescript is Enums. Prefer use of enums instead of string literals to ensure consisty throughout the application. Constants may solve the problem with string literals but enums also offers intellisense of what type of values it holds inside. + +**NOTE:** Never ever user enums as getters in a smart component, NEVER! + +## @Inputs & @Outputs + +- Consider placing @Input() or @Output() on the same line as the property it decorates +- Don't pass hard coded values into your components. Make them re-usable by adding inputs with default values. +- Avoid pointless aliasing of @Input and @Output +```ts +/* Avoid pointless aliasing */ +@Output('changeEvent') change = new EventEmitter(); +@Input('labelAttribute') label: string; + +/* Cleaner without aliasing */ +@Output() change = new EventEmitter(); +@Input() label: string; +``` + +- Avoiding prefixing @Output with `on` + +```ts +/* Avoid */ +@Output() onUpdateAppointment = new EventEmitter(); + +/* Prefer */ +@Output() updateAppointment = new EventEmitter(); +``` + +## API Calls & Response + +Read this: [API Calls & Response in Single Page Applications](/frontend/architecture/spa-applications-architecture.html#api-calls-response). + +## General Guidelines + +- Do not nest directories unneccessarily. +- Create files for constants i.e. LocalStorage Constants etc +- Refactor common methods to services +- Use SwitchMap when nesting observables, helps in writing a much cleaner code +- Use ts-lint and prettifier and format your code properly +- Don’t use JS to get reference of HTML elements e.g. `document.getElementById`, the are better ways of doing it in Angular +- Don’t generate HTML from a method as strings, use components or directives instead +- Use find method instead of iterating and finding an item out of an array conditionally +- Use ngAfterViewInit if you want to get reference to an element in the template on component load + - Avoid using timeouts for such cases with hard coded wait time + +## Services + +Instead of providing a service into the module if it’s going to be the part of core module then use providedIn: ‘root’ attribute inside a decorator else provide it in the feature module if its score is limited to the module itself. For example +@Injectable({ + providedIn: 'root' +}) +export class UserService { } + +If not used a in service or component this Service will get tree-shaken in the final build and won't be included in bundle.js + + +## Shared Components + +If a component is used in more than 1 modules move it into shared module with respective directory. For example +If **choose-patient** modal is used in **appointment** and **invoicing** module, in shared module you can move it either **appointment** directory(because primarily it was part of appointment module) or better move it into **patients** directory e.g. **shared/components/patients/choose-patient-modal** + +## RxJS +Angular 6+ uses **RxJS 6** internally so let’s make a habbit of using it in our application as well. + +Best part of **RxJS 6** is that you import only what you need, without having to import the whole rxjs modules like before. + +If you want to read What changed in RxJS 6? Go ahead and read [this](https://www.academind.com/learn/javascript/rxjs-6-what-changed/) article. + +Since RxJS 6 has some breaking changes and rxjs-compat doesn't cover those. We need to make sure if we are facing any issue related to RxJS, we need to refer to this guide: [RxJS v5.x to v6 Update Guide](https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/migration.md) + + +Refer to this site for the interactive changes from RxJS 5 -> RxJS 6 [RxJS Explorer](https://reactive.how/rxjs/explorer) + +