Angular Architecture/Best Practices Flashcards
software requirements
Node js, Angular CLI, VS code
Architecture Considerations
APP OVERVIEW - what is the application for, what are the goals, how is the client going to use it, what are the business and strategic benefits
APP FEATURES
DOMAIN SECURITY - are we using roles on the server side, groups, claims; how are they going to be communicated to angular app; how is angular going to communicate with our api, is it going to use token based or ldap with active directory
DOMAIN RULES - are they going to run on client side or server side. we need to run on both sides is good for validations. there were might be some number crunching ones, which might need api call and run on the server
LOGGING: when error in front end application
api or integrate with cloud
it helps in bug support and also have good statistics about the app
SERVICE/COMMUNICATION: how angular app is going to talk to the server
normally it is http/https
if your app might have real time data considerations then web sockets might be
not just thinking about data exchange between front end and back end but thinking about between service and components is helpful
DATA MODEL: api to angular; what are we passing to components
are we getting what we want from the api. in many cases data models with api give lot of data, we might only need subset of the data;
we might need to think if we are going to use viewmodel, models only the component needs
FEATURE COMPONENTS: what are our feature components and how are we going to structure our feature components
component commucation -
we might need to know key features upfront
SHARED FUNCTIONALITY- any 3 rd party components that we are going to use
are we going to use them directly or use a wrapper
shared in just one app or use across app; then we think about libraries
Architecture planning Template
https://docs.google.com/presentation/d
/1jrkbbB3eRtTOrbkaljQxLuypHsdUHDl3_
4uFFnHTSNo/edit#slide=id.p4
https: //codewithdan.me/angular-architecture-planning
https: //codewithdan.me/angular-architecture-planning-example
Domain Security
using tokens
using HttpInterceptor could be used to set auth header
what is the back end that issues token, and refresh tokens
Domain Rules
validation
Route guard for not to access without login
validate credentials
Logging
Azure app insights angular service for logging Handle errors based on different environment dev/stage/prod i used ngx-logger nlog - graylog
Services/communication
restful services -on back for this project node.js is being used. it could be java asp.net core, php Angular services below: Data service : use http client ; for customer/orders and login/logout(auth) Sorting Service Filtering Service Logger Service Mediator/bus if needed Component to component communication
HttpInterceptor will set token for HTTP requests if token GET/PUT/POST/DELETE/PATCH - to set header
Use PATCH - to update specific properties - one or two properties
Data Models
Application Models/Interface
Create class or just use interface on the client side
if we use interface, there is zero impact on the bundle size for production
it is better to opt for interfaces unless the classes are more specialized; like if model has properties and models
if you are just using the data from the server or using the object to scale to a smaller object then interfaces are just fine on client side
Interface is great for intellisense or code help
How to reduce the bundle size
How to reduce the bundle size
Feature Components
decide what are angular components needed for features of the app
Shared Functionality
Toast Modal Dialog Google map pager Menu Grid/cards
are these just feature used once or used in multiple feature components
this decides where to pull these in the folder hierrachy
if they are going to used by other applications then we could have a shared library
any 3 rd party components like prime Ng Ng Bootstrap Angular Material ag-grid
Take time for architecture it helps
you will be so much ahead in planning the application architecture
The folder layout
the reuse of code
we might change, as we are still in the initial phase
angular style guide
https://angular.io/guide/styleguide coding conventions naming rules application sturcture organizing modules creating and using components creating and using services life cycle hooks
Other considerations
Accessibilty - may be design consideration
i18n - Internationalization
Environments-
CI/CD
CDN/Container/Server - how are we deploying code to production
course on containerizing angular applications with docker
Unit testing- built in cli tools, karma test runner
End to end testing - protractor or Cypress
this might impact your design; the testers might prefer to have id on all the tags to make it easy to find
API’s - backend where the security happens; data validation
Performance of the application
Documentation
Organizing Features
Do structure the app such that you can LOCATE code quickly, IDENTIFY the code at glance, keep the FLATTEST structure you can, and TRY to be DRY (Do not repeat yourself)
LIFT
LOCATE CODE QUICKLY-
easy to structure the folder structure; once we get bundles it does not matter
IDENTIFY THE CODE AT A GLANCE- how we name our files like feature.component.ts data.service.ts canactivate.guard.ts = for route guards
KEEP THE FLATTEST STRUCTURE YOU CAN-
do not keep nested folders
Try to be DRY:
like we could use services to reuse
we could reuse directives and components
Organizing files - convention based - may be not recommended if there are multiple features
like putting all the components in one folder
putting all the services in one folder
like MVC pattern
Follow strict naming conventions
Related code may be separated
can result in a lot of files in a folder in larger applications
views would be different places
Organizing files - Feature based - Recommended and CLI does by default
May be you could have Feature folder like Customers
and have many components like edit,grid,cards
that are part of the feature
if there are too many then you could have subfolder called components like feature- Customer - Components - put all the components related
it has one extra layer in the es2015 import statement
Features are organized into their own folders
Features are self-contained
Easy to find everything related to a feature
Organizing files - Feature based -how to implement
use angular cli to generate initial feature folder/component ; you could even flatten without creating sub folders
Minimum of one Module per feature (as appropriate) - it helps in lazy loading ; it helps in features to be self-contained; it could be imported at the root module or some other feature if needed ;it is good for maintainence
Avoid Deeply nested folders
Flatten Feature hierrachies - application might have hierrachies like Course-exercise-lab but your code should have exercise folder, course folder, items folder instead of having them in hierarchy
Flatten out top level features as much as possible; this will reduce the need to add ../,../ in the code
if you have nested features that is okey, but be cautious about how deeply you want to nest
Flattening makes - imports to be easier, refactor to be easier, easy to find components
Feature Modules
let us assume we have customer feature and orders feature
ng g c customer = it creates customer component with a folder created at the top level
people normally add this feature in the root module; there is nothing wrong in it as the app grows and grows and gets more feature;
we have one module, we can not use lazy loading and the features are no longer self-contained
usually we could have one module per feature but often instructor has 2
Feature has routing; so we have feature routing module defined with in a module in the feature itself customers-routing.module.ts that would be imported into module for this feature customer.module.ts
if you have complex subfeatures, you would want them to be self-contained, if the portion of the team does it. root module does not need to worry about it
so we are going to have feature-routing module and feature module
Angular Style Guide-Single responsibility
Apply the single responsibility principle (SRP) to all components, services, and other symbols. This helps make the app cleaner, easier to read and maintain, and more testable.
Do define one thing, such as a service or component, per file.
Consider limiting files to 400 lines of code.
Why? One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control.
Why? One component per file avoids hidden bugs that often arise when combining components in a file where they may share variables, create unwanted closures, or unwanted coupling with dependencies.
Why? A single component can be the default export for its file which facilitates lazy loading with the router.
The key is to make the code more reusable, easier to read, and less mistake prone.
ASG - Small functions
Do define small functions
Consider limiting to no more than 75 lines.
Why? Small functions are easier to test, especially when they do one thing and serve one purpose.
Why? Small functions promote reuse.
Why? Small functions are easier to read.
Why? Small functions are easier to maintain.
Why? Small functions help avoid hidden bugs that come with large functions that share variables with external scope, create unwanted closures, or unwanted coupling with dependencies.
ASG -Naming
Do follow a pattern that describes the symbol’s feature then its type. The recommended pattern is feature.type.ts.
Naming conventions are hugely important to maintainability and readability. This guide recommends naming conventions for the file name and the symbol name.
General Naming Guidelines
Style 02-01
Do use consistent names for all symbols.
Do follow a pattern that describes the symbol’s feature then its type. The recommended pattern is feature.type.ts.
Why? Naming conventions help provide a consistent way to find content at a glance. Consistency within the project is vital. Consistency with a team is important. Consistency across a company provides tremendous efficiency.
Why? The naming conventions should simply help find desired code faster and make it easier to understand.
Why? Names of folders and files should clearly convey their intent. For example, app/heroes/hero-list.component.ts may contain a component that manages a list of heroes.
ASG-Separate file names with dots and dashes
Do use dashes to separate words in the descriptive name.
Do use dots to separate the descriptive name from the type.
Do use consistent type names for all components following a pattern that describes the component’s feature then its type. A recommended pattern is feature.type.ts.
Do use conventional type names including .service, .component, .pipe, .module, and .directive. Invent additional type names if you must but take care not to create too many.
Why? Type names provide a consistent way to quickly identify what is in the file.
Why? Type names make it easy to find a specific file type using an editor or IDE’s fuzzy search techniques.
Why? Unabbreviated type names such as .service are descriptive and unambiguous. Abbreviations such as .srv, .svc, and .serv can be confusing.
Why? Type names provide pattern matching for any automated tasks.
Self-contained Features - do not put everything in app.module
with module, all the components and the routing module
self contained feature let different members of a team work on given feature and own it . they are incharge of routing, any services
we just need to import the customer/feature module into the root module i.e, app.module.ts
we need not need to know any details about the feature
exception is if we are lazy loading, we need to define the route and own the root module
any child route or submodules in the customer module, we need not know anything about it. we could just import them
Lazy loading
unless you use lazy loading; all the modules are eagerly loaded that means all the modules and their associated components, directives, pipes and services must be downloaded from the server when the user first visits the application
This takes lot of time
Eager loading
eagerly loaded that means all the modules and their associated components, directives, pipes and services must be downloaded from the server when the user first visits the application
Asychronous routing
https://csharp-video-tutorials.blogspot.com/2018/12/lazy-loading-in-angular.html
loads the feature modules lazily on demand . This can significantly reduce the initial load time of your application
We want to lazily load EmployeeModule. To lazy load a module, it has to meet 2 requirements.
All the routes in the angular module that you want to lazy load should have the same route prefix
const appRoutes: Routes = [
{
path: ‘employees’,
children: [
{ path: ‘’, component: ListEmployeesComponent },
{ path: ‘create’, component: CreateEmployeeComponent },
{ path: ‘edit/:id’, component: CreateEmployeeComponent },
]
}
];
The module should not be referenced in any other module. If it is referenced, the module loader will eagerly load it instead of lazily loading it. Our application already meets the first requirement. All our EmployeeModule routes have the same route prefix i.e employees.
we are removing the employee module reference in app.module.ts we remove it in the imports on the top and imports section as well
EmployeeModule that we want to lazy load is referenced in the AppModule. So please delete the EmployeeModule references in the AppModule (app.module.ts)
we could check all the references by rightclicking on the module name and find all references
Eager loading - in the network tab; there is lot of data loaded and the number of request are more
Eager loading - in the network tab; there is lot of data loaded and the number of request are more
use loadChildren property for lazy loading
in app.module.ts
{path:’employees’,loadchildren:’./employee/
employee.module#EmployeeModule’}
after hash it is the Employee Module class name
in Employee Routing module:
const appRoutes: Routes = [
{
path: ‘employees’,
children: [
{ path: ‘’, component: ListEmployeesComponent },
{ path: ‘create’, component: CreateEmployeeComponent },
{ path: ‘edit/:id’, component: CreateEmployeeComponent },
]
}
];
so the URL needs employees name twice
localhost:4200/employees/employees
we could remove the path:'employees' in the employee module After you remove the 'employees' parent route, the routes should look as shown below.
const appRoutes: Routes = [
{ path: ‘’, component: ListEmployeesComponent },
{ path: ‘create’, component: CreateEmployeeComponent },
{ path: ‘edit/:id’, component: CreateEmployeeComponent },
];
with lazy loading
https://www.youtube.com/watch?v
=75XFBIKLPQY
https://csharp-video-tutorials.blogspot.com
/2018/12/lazy-loading-in-angular.html
load time will be lower; transferred size of data
we could see all in network in chrome developer tools
with lazy loading the modules are loaded on demand we could see request for module load when navigated to the route
about feature module
it has routing module - with does not have anything in the path the routing module is imported into about module in the imports - we have RouteModule.forchild(appRoutes)
import all the components in the feature routing module:
import { AboutComponent } from ‘./about.component’;
const routes: Routes = [
{ path: ‘’, component: AboutComponent }
];
@NgModule({ imports: [ RouterModule.forChild(routes) ], exports: [ RouterModule ] }) export class AboutRoutingModule { static components = [ AboutComponent ]; }
we could import the static variale in the feature module:
import { NgModule } from ‘@angular/core’;
import { AboutRoutingModule } from ‘./about-routing.module’;
@NgModule({
imports: [ AboutRoutingModule ],
declarations: [ AboutRoutingModule.components ]
})
export class AboutModule { }
—
it is not loaded into app.module.ts but loaded into app-routing.module.ts with lazy loading
{path:’about’,loadchildren:’app/about/about.module#AboutModule’}
Core and Shared Modules - DRY code - Do not Repeat Yourself code -CORE MODULES
Core Module:
Core folder should contain singleton services shared throughout app
Services that are specific to a feature can go in the feature’s folder. we could create folder called services in the feature folder and this services is imported in the feature module. in this case the services is not used else where or injected else where in the application
example: LogginService; ErrorService - which intercepts the errors in general; DataService-one specific to customers and one specific to orders
we could call it Core or some people call it Common; but what matters is how we are organizing the code and have everyone on the same page
Core and Shared Modules - DRY code - Do not Repeat Yourself code -SHARED MODULES
Shared Modules -
Reusable components, pipes, directives
example- CalendarComponent
AutoCompleteComponent
may be custom grid that could be used through out the app
will this widget be used only in this app or used across apps? if it is so generic and could be used across applications then we should be creating angular library
Core and Shared Modules importing
Shared Modules may be imported multiple times into RootModule, Feature Modules
Core Module - it is imported only once into the root module (app module)
Preventing Reimport of Core
Core should only be imported into the root/app module
export function throwIfAlreadyLoaded (parentModule:any, moduleName:string){ if(parentModule){ throw new Error('${moduleName} has already been loaded. Import core modules in the AppModule only.') } }
–
Core Module:
import {throwIfAlreadyLoaded } from ‘./import.guard’
export class CoreModule{
constructor(@Optional() @SkipSelf() parentModule:CoreModule)
{
throwIfAlreadyLoaded(parentModule,’CoreModule’);
}
}
in constructor we are having dependency injection
@Optional(): A constructor parameter decorator that marks a dependency as optional; it is okey if there is nothing for this to inject
@SkipSelf()- go to the parent injector and see if we can inject a Core Module
A constructor parameter decoarator that tells the DI framework that dependency resolution should start from the parent injector
skipping self and going up to the parent
we hope that there is no parent module; this way we could have core module not imported into parent module
Using the Base Class: CoreModule can derive from EnsureModuleLoadedOnceGuard
export class EnsureModuleLoadedOnceGuard { constructor(targetModule: any) { if (targetModule) { throw new Error('${targetModule.constructor.name} has already been loaded. Import this module in the AppModule only.'); } } } Alternative Aoproach - Base Class An alternative to throwlfAlreadyLoaded is to convert it into a class
---- import { EnsureModuleLoadedOnceGuard } from './ensure-module-loaded-once.guard'; export class CoreModule extends EnsureModuleLoadedOnceGuard { constructor(@Optional() @SkipSelf() parentModule: CoreModule) { super(parentModule); }
if there is parent module that means it is bad, that measns core module is already imported from someother module and we do not want that
using class instead of function if there are other module that needed to be loaded in one particular place the appModule
General folders in Core Folder
Growler(type of toast message), Interceptors,modal, navbar, overlay, Services like auth.service, logger, trackby and other services and strategies for lazy loading
each of the above have their own module
as there is lot going on with them
it helps in self-contained, test them, use them on their own
if it is simple them then import them into core directly instead of creating module for them
core usually has services but any components that are used only once could go in here
Core folder also has components to use in app-component;
Core - is for single usage
//type of toast message //modal dialog to reuse in the whole app Loading
all these components are needed only single time in the app, like singleton. they do not have to be
anything in the core is used one time in the application
people should remember to not use these selectors for the components in multiple places in the application
some people put the components in shared folder
Shared folder - all here could be reused
■ directives ■ filter-textbox
map N pagination N pipes • interfaces.ts • mocks.ts ✓ router.animations.ts • shared.module.ts
map component - used multiple times in the app
all the above could be reused many times
Creating a Custom Library
if one of your shared module, you want to put in on npm or internal npm to be used by other applications
$ ng new my-project //this creates workspace with one project
$ ng generate library my-lib //custom shared library
Creating a Custom Library:
Angular CLI provides library functionality
The tsconfig file will be updated to look for library reference
Build the library before trying to use it in the workspace
library creates a separate project adds into the existing workspace
so we could test the library with out having to publish into npm, just by using our web project
ng build my-lib //to build the library
now we could test the project
tsconfig. json = it adds path s to the dist folder of the project where we going to build; typescript looks at this file when you try to build
angular. json = it has workspace and the two projects that we have created
Publish a custom library
package.json file will be used to publish to npm
npm publish = this publishes to npm but it needs login
if you are publishing to local npm then you might need path
$ ng build my-lib $ cd dist/my-lib $ npm publish
Publishinc a Custom Liorary
Build your library for production
Publish to npm or to an internal npm
https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry
library project
it has component, module and service samples created
Consuming a custom library
we import into the web project app.module.ts in the array
then the src/public_api = of library gets imported; this exposes all the component, services in the library
we could replace it with package name ; like acme-shared
we could use the library directly in the web project with tag
we need to then bulid the acme-shared project
ng build acme-shared –watch and not the root project
then ng serve -o
then we could push npm
we have created dist folder for the library, we could see the package.json in the folder which has the version name and the name of the package that we will be publishing
we need to run publish with npm command in dist - shared folder
when to go with custom project library
if we see that some widgets are being reused in many apps
then we could creaetd libaray instead of creating them in shared folders
then npm install custom shared package
Widget - Shared or Feature
widget - like menu, calendar,auto complete
if a widget is used by many component then we put it in Shared Module
if a widget is used only in a specific feature component then we put it in Feature Module
Feature/Child Componet
this goes into Feature Module either feature or child to a feature
Services
if reusable put it in core module if a particular service is only use by specific feature then it goes into Feature module
Routed components
the path defined to a specific component, it goes in routing module
Routing module
this will be imported into the feature module
Pipes
in shared module
if only a feature uses a pipe then put in feature module
Exporting of Feature Module
Feature module is a stand alone, it is usually imported into Root module and it does not need to know anything. we will not be exporting anything typically from Feature Module
Routing Module
we will importing routing functionality and export that
Core Module
this is going to imported into root module this will not have any exports Exception is that if we put single use components like menu that should be used one time in the app; in this case we need to export that component
Shared Module
it needs exports; if we need to import in any of the modules
Shared library
we could import when needed in feature module or in the shared module
Container - Presentation pattern for components
if there is a big component then we break it down to child components
Container component - which has mutiple presentation component
Container component manages the state (interact with service/store); it is boss/manager
Presentation component - presnts/renders the state
the presentation component do not get the state; they just get what container component gives to them
Container components gets the data; it might possibily render something as well like heading; but it will hand the data down to presentation component for UI
child components - do not know how to get the data or how to interact with store; they are just presentation component
Container - Presentation pattern for components- Child Routes and Components
Tool bar or Tabbed interface customer: /customers/44 Child : /customers/44/edit child:/customers/44/details
const routes: Route = [
path: ‘customers/:id’, component: CustomerComponent, children: [
{ path: ‘edit’, component: CustomerEditComponent },
{ path: ‘details’, component: CustomerDetailsComponent },
]
}
];
do not build components, very big; break them into child components
if child component has lot of data having a separate child route will help to load the data only on demand
working with Container/Presentation components or Parent and Child components
Pass state from container —> presentation using input properties
Communicate from presentation -> container using output properties
Set change detection strategy to OnPush in presentation components
Consider cloning complex types
Use child routes as appropriate
we are using input properties to call the child components
in customer componnet html
<div> </div>
using router for child components
instead of input properties on parent component, we will have router outlet
in the container- presentation pattern
we load all the data at once and show and hide based on the need
we do not have separate routes
in the parent- child pattern
we have routes set up and we are going to load the child data only on demand when the route is reached
we could bookmark exact route with this way
also if we have lot of data then it is better to load them on demand by using routes
Data transfer from component to template
Property binding [] or Interpolation {} = from component code to Template
Event Binding = to get data from Template to Component