Before we dive into the Angular code, here is the UML diagram of how we are planning to implement a session manager using strategy pattern.

UML Diagram for SessionManager

SessionStrategy is an interface that is implemented by storage classes like LocalStorage and SessionStorage. SessionContext class will just refer this strategy interface, thus making SessionContext class independent of how strategies are implemented.

We will then be creating a client, whose responsibility would be to initiate a specific session strategy object, like LocalStorage/SessionStorage and pass it to the context. It would be something like this:

const context = new SessionContext(<YOUR-PREFFERED-STORAGE-CONFIG-CLASS>);

const session = context.loadStorage();

const token = session.get('access_token');

The benefits of using strategy pattern here is that we have separated the concerns, can add on different storage classes with time and avoided complex conditions to initiate them every time.

Now let’s jump to Angular now. But before that, I think we have seen this code so many times:

const routes: Routes = [{ path: 'heroes', component: HeroesComponent }]; NgModule ({ imports: [RouterModule.forRoot(routes)], exports: [ RouterModule })

export class AppRoutingModule { }

We can see a RouterModule forRoot function which accept routes of type Routes, that we can define in every project.

So basically the router takes an array of routes and a URL, and tries to create a RouterState. All this with an intention that application can render the component when navigating to a matching url.

The point of sharing this piece of code is to showcase how forRoot() takes a service configuration object and returns a ModuleWithProviders, which is a simple object with the properties like ngModule, and providers.

Let’s write our SessionManagerModule now. It should either access local storage or session storage based on our configurations. First, we create a session-manager module, and create this folder structure within it. I will explain the purpose of these folders as I go along.

session-manager

- config

- storages

- strategy

session-manager.module.ts

session.service.ts

So in the storages folder, we will have our two storage classes, LocalStorage and SessionStorage. Each class will have functions to read/write the browser storages.

However, before writing those classes, let us create an interface called SessionStrategy that will be implemented by both the classes.

session-manager

- config

- storages

- strategy

- session-strategy.ts

session-manager.module.ts

session.service.ts

We created a session-strategy.ts file and and now let’s write that class.

export interface SessionStrategy { get(key: string); set(key: string, value: string | Object); remove(key: string); removeAll(); }

Now we got back to the storages folder and create two more classes:

session-manager

- config

- storages

- local-storage.ts

- session-storage.ts

- strategy

- session-strategy.ts

session-manager.module.ts

session.service.ts

We will be implementing the SessionStrategy in our two new classes:

import { SessionStrategy } from '../strategy/session-strategy'; export class LocalStorage implements SessionStrategy { static setup(): any { return LocalStorage; } public getStorage() { return window.localStorage; } public get(key: string): any { return this.getStorage().getItem(key); } ...

And the SessionStorage class:

import { SessionStrategy } from '../strategy/session-strategy'; export class SessionStorage implements SessionStrategy { static setup(): any { return SessionStorage; } public getStorage() { return window.sessionStorage; } public get(key: string): any { return this.getStorage().getItem(key); } ...

Now let’s create a context class file in the strategy folder.

session-manager

- config

- storages

- local-storage.ts

- session-storage.ts

- strategy

- session-strategy.ts

- session-context.ts

session-manager.module.ts

session.service.ts

Context class will just refer the strategy interface for executing the strategy and its functions.

import { SessionStrategy } from './session-strategy'; export class SessionContext { public sessionStrategy: SessionStrategy; constructor(sessionStrategy: SessionStrategy) { this.sessionStrategy = sessionStrategy; }



public loadStorage() {

return this.sessionStrategy;

} }

Now we will be creating a client, i.e. a specific session strategy object and pass it to the context. Our SessionManagerModule will replace the strategy associated with the context when we make our module configurable.