Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: deployment scenarios #398

Merged
merged 3 commits into from
Oct 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ kb_sync_latest_only
- [Concept - Localization](./concepts/localization.md)
- [Concept - SEO](./concepts/search-engine-optimization.md)
- [Guide - Forms](./guides/forms.md)
- [Concept - Deployment Scenarios for Angular Applications](./concepts/deployment-angular.md)

### Developing

Expand Down Expand Up @@ -55,8 +56,10 @@ kb_sync_latest_only

### Setup

- [Concept - Deployment Scenarios](./concepts/deployment-scenarios.md)
- [Guide - SSR Parameters](./guides/ssr-startup.md)
- [Concept - Building Blocks of the Intershop PWA](./concepts/pwa-building-blocks.md)
- [Guide - Building and Running Server-Side Rendering](./guides/ssr-startup.md)
- [Guide - Building and Running nginx Docker Image](./guides/nginx-startup.md)
- [Concept - Multi-Site Handling](./concepts/multi-site-handling.md)
- [Concept - Hybrid Approach](./concepts/hybrid-approach.md)
- [Guide - Hybrid Approach and ICM URL Rewriting](./guides/hybrid-approach-icm-url-rewriting.md)
- [Concept - Logging](./concepts/logging.md)
Expand Down
85 changes: 53 additions & 32 deletions docs/concepts/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,36 @@ As can be seen here, only build-time and deploy-time configuration parameter can

### Node.js Environment Variables

When running the application in Angular Universal mode within a _Node.js_ environment, we can additionally access the process environment variables via _process.env._ This method provides a way to configure the application at deploy time, e.g., when using docker images.
Configuration can then be consumed and passed to the client side via means of state transfer.
When running the application in Angular Universal mode within a _Node.js_ environment, we can additionally access the process environment variables via _process.env_.
This method provides a way to configure the application at deploy time, e.g., when using docker images.
Configuration can then be consumed and passed to the client side via state transfer using Angular's [TransferState](https://angular.io/api/platform-browser/TransferState).

To introduce a new `TransferState` key it should be added to the [`state-keys.ts`](../../src/app/core/configurations/state-keys.ts).

```typescript
export const NEW_KEY = makeStateKey<string>('newKey');
```

The actual transfer is handled by the [`app.server.module.ts`](../../src/app/app.server.module.ts) where the mapping of the environment variable is done.

Accessing the transferred state in a service or a component is pretty straight forward:

```typescript
import { isPlatformBrowser } from '@angular/common';
import { PLATFORM_ID } from '@angular/core';
import { NEW_KEY } from 'ish-core/configurations/state-keys';

newKey string;

constructor(
@Inject(PLATFORM_ID) private platformId: string,
private transferState: TransferState,
) {}

if (isPlatformBrowser(this.platformId)) {
this.newKey = this.transferState.get<string>(NEW_KEY, 'default value');
}
```

### NgRx Configuration State

Expand Down Expand Up @@ -80,7 +108,9 @@ Deployment settings do not influence the build process and therefore can be set
The main criteria of this category is the fact that deployment settings do not change during runtime.
The most common way of supplying them can be implemented by using Angular CLI environment files and `InjectionToken`s for distribution throughout the application's code.

An example for this kind of settings are breakpoint settings for the different device classes of the application touchpoints.
An example for this kind of settings are breakpoint settings for the different device classes of the application touch points.

Deployment settings can also be set by using `TransferState` to use environment variables of the deployment for the configuration of the application.

### Runtime Settings

Expand All @@ -91,45 +121,32 @@ Therefore only NgRx means should be used to supply them.
Nevertheless, default values can be provided by environment files and can later be overridden by system environment variables.

Everything managed in the NgRx state is accumulated on the server side and sent to the client side with the initial HTML response.
The reason for this is that this is the most common deployment scenario of PWAs (see [Concept - Deployment Scenarios](./deployment-scenarios.md)).

## Multi-Site Handling
To access these properties we provide the [`StatePropertiesService`](../../src/app/core/utils/state-transfer/state-properties.service.ts), which takes care of retrieving the configuration either from the configuration state, an environment variable or the environment.ts (in that order).

### Configurations REST Resource

ICM provides a Configurations REST resource - `/configurations` - that is supposed to provide all relevant runtime configurations that can be defined in the ICM back office and are required to configure a REST client as well.
This includes service configurations, locales, basket preferences, etc.

The ICM configurations information can be accessed through the [`getServerConfigParameter`](../../src/app/core/store/general/server-config/server-config.selectors.ts) selector.

Since version 0.9 of the PWA there are means to dynamically configure ICM channel and application to determine the correct REST endpoint for each incoming top level request.
Nevertheless, you can still configure it in a static way for each PWA deployment via Angluar CLI environments.
## ICM Endpoint Configuration

### Setting the Base URL

At first, the PWA has to be connected with the corresponding ICM.
This can be done by modifying environment files or by setting the environment variable `ICM_BASE_URL` for the process running the _Node.js_ server.
The latter is the preferred way.
See also [Guide - Building and Running Server-Side Rendering](../guides/ssr-startup.md)

Independent of where and how you deploy the Angular Universal application, be it in a docker container or plain, running on Azure, with or without service orchestrator, setting the base URL provides the most flexible way of configuring the PWA.
Refer to the documentation for mechanisms of your environment on how to set and pass environment variables.

### Static Setting for Channels
### Settings for Channels and Applications

Use the properties `icmChannel` and `icmApplication` in the Angular CLI environment or the environment variables `ICM_CHANNEL` and `ICM_APPLICATION` to statically direct one deployment to a specific REST endpoint of the ICM.

### Dynamic Setting of Channels

To set ICM channels and applications dynamically, you have to use URL rewriting in a reverse proxy running in front of the PWA instances.
The values have to be provided as URL parameters (not to be confused with query parameters).

**nginx URL rewrite snippet**

```text
rewrite ^(.*)$ "$1;channel=inSPIRED-inTRONICS_Business-Site;application=-" break;
```

The above example configuration snippet shows a [Nginx](https://en.wikipedia.org/wiki/Nginx) rewrite rule on how to map an incoming top level request URL to an internal worker process, e.g., _Node.js_.
It shows both the PWA parameters `channel`, `application` and their fixed example values.
The parameters of each incoming request are then read and transferred to the NgRx store to be used for the composition of the initial HTML response on the server side.
Afterwards they are propagated to the client side and re-used for subsequent REST requests.

In the source code of the project we provide an extended [Nginx](https://en.wikipedia.org/wiki/Nginx) docker image for easy configuration of multiple channels via sub-domains.
Refer to our Gitlab CI configuration (file _.gitlab-ci.yml)_ for a usage example.

## Feature Toggles

To activate additional functionality, we use the concept of feature toggles throughout the application.
Expand All @@ -140,7 +157,7 @@ Of course, the ICM server must supply appropriate REST resources to leverage fun
### Configuring Features

The configuration of features can be done statically by the Angular CLI environment property `features` (string array) or the environment parameter `FEATURES` (comma-separated string list).
To configure it dynamically, use the PWA URL parameter `features` (comma-separated string list) during URL rewriting in the reverse proxy.
To configure it dynamically, use the PWA URL parameter `features` (comma-separated string list) during URL rewriting in the reverse proxy. (see [Concept - Multi-Site Handling][concept-multi-site])

### Programmatically Switching Features

Expand Down Expand Up @@ -193,7 +210,7 @@ Switching features in tests can be triggered by calling [`FeatureToggleModule.sw

You can set the default locale statically by modifying the order of the provided locales in the Angular CLI environment files.
The first locale is always chosen as the default one.
To dynamically set the default locale, use the URL parameter `lang` when rewriting the URL in the reverse proxy (see Dynamic Setting of Channels).
To dynamically set the default locale, use the URL parameter `lang` when rewriting the URL in the reverse proxy (see [Concept - Multi-Site Handling][concept-multi-site]).

## Extend Locales

Expand All @@ -213,6 +230,10 @@ export class ConfigurationModule {
}
```

> ### Configuration REST Resource
>
> We are currently planning to implement a Configuration REST resource in the ICM so that all necessary runtime configuration can be defined in the ICM Backoffice and consumed by each PWA deployment.
# Further References

- [Concept - Multi-Site Handling][concept-multi-site]
- [Guide - Building and Running Server-Side Rendering](../guides/ssr-startup.md)
- [Guide - Building and Running nginx Docker Image](../guides/nginx-startup.md)

[concept-multi-site]: ./multi-site-handling.md
Binary file not shown.
Binary file not shown.
57 changes: 57 additions & 0 deletions docs/concepts/deployment-angular.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!--
kb_concepts
kb_pwa
kb_everyone
kb_sync_latest_only
-->

# Deployment Scenarios for Angular Applications

## Simple Browser-Side Rendering

Angular applications are built for a [static deployment][angular-deployment] by default.
When running `ng build` in any shape or form, the build output is aggregated in the `dist` folder and the resulting files can be statically served using any HTTP server that is capable of doing that (only a [fallback](https://angular.io/guide/deployment#routed-apps-must-fallback-to-indexhtml) for `index.html` has to be configured).

When the application is served this way, the initial page response for the browser is minimal (`index.html` with scripts inserted) and the application gets composed and rendered on the client side.

![Angular-BrowserSideApp-Sequence](deployment-angular-browsersideapp-sequence.jpg 'Angular-BrowserSideApp-Sequence')

Of course, this can have a significant impact on the client side if no efficient rendering power is available.
Search engine crawlers might also not be able to execute JavaScript and therefore might only see the initial minimal response.

This method of deployment is suitable for demo servers supplying a fast build chain.
However, we do not recommend this setup for production use.

> This setup is also used for the Angular CLI built-in webpack-dev-server and should not be confused with a standard production setup.

## Browser-Side Rendering with On-Demand Server-Side Pre-Rendering (Angular Universal)

To tackle sophisticated SEO requirements, the [Angular Universal][angular-universal] package was introduced.
In a second step in the build process, a server-side application can be built by the Angular CLI after building the client-side application.
The resulting distribution has to be executed in a node environment.
The _server.js_ executable handles client requests on the server and pre-renders the content of the page, basically executing everything in a _Node.js_ environment.
The resulting initial page response to the browser is mainly prepared and can be displayed quickly on the client side while the client-side application is booting up.

![Angular-ServerSideRendering-Sequence](deployment-angular-serversiderendering-sequence.jpg 'Angular-ServerSideRendering-Sequence')

This method is the default setup for the Intershop PWA.
We provide a `Dockerfile` for building the [SSR Image][concept-building-blocks].

## Impact of Service Workers

If the Intershop PWA is run with an enabled [Service Worker][concept-progressive-web-app], the SSR process is only triggered for the first visit to the web page.
After that, the service worker lets the application behave like a simple browser-side application, where certain additional caching can lead to an improved client experience.

However, browsers and crawlers that do not support JavaScript execution will still receive fully pre-rendered page responses from the SSR process.

# Further References

- [Concept - Building Blocks][concept-building-blocks]
- [Concept - Progressive Web App][concept-progressive-web-app]
- [Angular - Deployment][angular-deployment]
- [Angular - Universal][angular-universal]

[angular-deployment]: https://angular.io/guide/deployment
[angular-universal]: https://angular.io/guide/universal
[concept-building-blocks]: ./pwa-building-blocks.md
[concept-progressive-web-app]: ./progressive-web-app.md#service-worker
38 changes: 0 additions & 38 deletions docs/concepts/deployment-scenarios.md

This file was deleted.

3 changes: 2 additions & 1 deletion docs/concepts/hybrid-approach.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ The server-side rendering process must be started with `SSR_HYBRID=1`.
In addition, the PWA must be run with secure URLs as well.
This can be achieved with `SSL=1`.

**Only for development environments**: It might be necessary to set `TRUST_ICM=1` if the used development ICM is deployed with an insecure certificate.
> :warning: **Only for development environments**: It might be necessary to set `TRUST_ICM=1` if the used development ICM is deployed with an insecure certificate.

Also, the **Service Worker must be disabled** for the PWA, as it installs itself into the browser of the client device and takes over the routing process, making it impossible to break out of the PWA and delegate to the ICM.

Expand Down Expand Up @@ -106,3 +106,4 @@ For this reason, the PWA must be adapted to work with the Responsive Starter Sto

- [Guide - Building and Running Server-Side Rendering](../guides/ssr-startup.md)
- [Guide - Handle rewritten ICM URLs in Hybrid Mode](../guides/hybrid-approach-icm-url-rewriting.md)
- [Concept - Multi-site handling](multi-site-handling.md)
5 changes: 5 additions & 0 deletions docs/concepts/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ Information logged to the console contains the following:
- Further the redirect actions of the [Hybrid Approach](./hybrid-approach.md) are logged with `RED <url>`.

- Uncaught `Error` objects thrown in the SSR process, including `HttpErrorResponse` and runtime errors are printed as well.

# Further References

- [Guide - Building and Running Server-Side Rendering](../guides/ssr-startup.md)
- [Guide - Building and Running nginx Docker Image](../guides/nginx-startup.md)
58 changes: 58 additions & 0 deletions docs/concepts/multi-site-handling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<!--
kb_concepts
kb_pwa
kb_everyone
kb_sync_latest_only
-->

# Multi-Site Handling

## Motivation

Imagine running an Europe-wide shop with Intershop, where the website is available in 16 different countries with different languages and a varying selection of products, but only little modification of the website appearance over all.
This would be set up by having 16 channels with one headless application each on the ICM side.

In a setup before the PWA 0.9 release this would have lead to a deployment with 16 PWA instances combined with one nginx for caching each.
Of course, a fail-over setup would increase the number of required deployment containers even more.

Therefore, starting from version 0.9, the PWA features means to dynamically configure ICM channel and application to determine the correct REST endpoint for each incoming top level request.

The aforementioned scenario can now be run with just one PWA instance and a reverse proxy running in front, implementing this dynamic configuration.

## Configuration

To set ICM channels and applications dynamically, you have to use URL rewriting in a reverse proxy running in front of the PWA instances.
The values have to be provided as URL parameters (not to be confused with query parameters).

**nginx URL rewrite snippet**

```text
rewrite ^(.*)$ "$1;channel=inSPIRED-inTRONICS_Business-Site;application=-" break;
```

The above example configuration snippet shows an [nginx](https://en.wikipedia.org/wiki/Nginx) rewrite rule on how to map an incoming top level request URL to a dynamically configured URL for the downstream PWA instance.
It shows both the PWA parameters `channel`, `application` and their fixed example values.
The parameters of each incoming request are then read by the PWA upon initialization and used for the composition of the initial HTML response on the server side.
Afterwards they are propagated to the client side and re-used for subsequent REST requests.

In the source code of the project we provide an [extended nginx](./pwa-building-blocks.md#pwa---nginx) Docker image for easy configuration of multiple channels via sub-domains.

## Multi-Site Production Deployment

These steps should give an overview of the internal workings:

![Current Deployment](pwa-building-blocks-production-deployment.svg)

1. The browser requests the page by URL from the reverse proxy. Nginx appends URL parameters to the incoming request URL for channel, application, etc depending on the incoming domain.

2. The node _express.js_ server runs Angular Universal pre-rendering for the requested URL. Angular Universal dynamically configures itself with the incoming parameters.

3. The requested page is filled with content retrieved via ICM REST API for this dynamically configured channel.

4. The configuration parameters are persisted for the client application via state transfer. Afterwards, the response is delivered to Nginx.

5. The response is delivered to the browser.

6. The initial page response is displayed to the user and the Angular Client application is booting up in the browser, configuring itself with the transferred parameters for channel, application, etc.

7. Once booted up, additional REST calls are directed to the matching ICM endpoint for the configured channel.
Loading